Compartilhar via


Incompatibilidade de SQL-CLR

LINQ to SQL automatiza muitas das conversões entre o modelo de objeto e o SQL Server. Entretanto, algumas situações impedem a conversão exata. Essas incompatíveis chave entre Common Language Runtime (CLR) tipo e os tipos de base de dados do SQL Server são resumidas nas seções a seguir. Você pode encontrar mais detalhes sobre mapeamentos de tipos específicos e tradução de funções no Mapeamento de Tipos e Funções do SQL-CLR e tipos de dados.

Tipos de dados

A conversão entre CLR SQL Server e ocorre quando uma consulta está sendo enviadas a base de dados, e quando os resultados são novamente enviado ao seu modelo de objeto. Por exemplo, a seguinte consulta de Transact-SQL requer duas conversões de valor:

Select DateOfBirth From Customer Where CustomerId = @id

Antes que a consulta pode ser executada no SQL Server, o valor para o parâmetro de Transact-SQL deve ser especificado. Nesse exemplo, o valor do parâmetro de id deve primeiro ser convertido de um tipo de CLR System.Int32 a um tipo SQL Server INT de modo que a base de dados pode compreender o que o valor é. Para recuperar os resultados, a coluna SQL Server DateOfBirth deve ser convertido de um tipo SQL Server DATETIME a um tipo de CLR System.DateTime para uso no modelo de objeto. Nesse exemplo, os tipos no modelo de objeto CLR e na base de dados SQL Server têm mapeamentos naturais. Mas, isto não é sempre o caso.

Contrapartes ausentes

Os seguintes tipos não têm correspondentes razoáveis.

  • Incompatíveis no namespace de CLR System :

    • Inteiros sem sinal. Esses tipos são mapeados normalmente a suas contrapartes assinados de tamanho maior para evitar estouro. Literais podem ser convertidos em um sinal numérico do mesmo ou de menor tamanho, com base no valor.

    • Boolean. Esses tipos podem ser mapeados para um bit ou um número maior ou uma cadeia de caracteres. Um literal pode ser mapeado para uma expressão que avalia para o mesmo valor (por exemplo, 1=1 em SQL para True em CLS).

    • TimeSpan. Este tipo representa a diferença entre dois valores de DateTime e não corresponde a timestamp SQL Server. CLR System.TimeSpan também pode mapear ao SQL Server TIME o tipo em alguns casos. O tipo do SQL Server TIME foi pretendido representar somente valores positivos menos de 24 horas. CLR TimeSpan tem um intervalo muito maior.

    Observação

    Os tipos específicos de .NET Framework do SQL Server em System.Data.SqlTypes não estão incluídos dentro nesta comparação.

  • Incompatíveis no SQL Server:

    • Tipos de caracteres de comprimento fixo. Transact-SQL faz distinção entre categorias Unicode e de Unicode e não tem três diferentes tipos em cada categoria: comprimento fixo nchar/char, comprimento variável nvarchar/varchar, e maior dimensionado ntext/text. Os tipos de caracteres de comprimento fixo podem ser mapeado para o tipo de CLR System.Char para recuperar caracteres, mas não correspondem realmente o mesmo tipo em conversões e comportamento.

    • Bit. Embora o domínio de bit tem o mesmo número de valores que Nullable<Boolean>, os dois tipos são diferentes. Bit pega os valores 1 e 0 de em vez de true/false, e não pode ser usado como um equivalente as expressões booleanas.

    • Timestamp. Ao contrário do tipo de CLR System.TimeSpan , o tipo do SQL Server TIMESTAMP representa um número de 8 bytes gerado pelo base de dados que é exclusivo para cada atualização e não é baseado na diferença entre valores de DateTime .

    • Money e SmallMoney. Esses tipos podem ser mapeados para Decimal mas são basicamente tipos diferentes e são tratados como esta'n por funções e conversões pelo base.

Vários mapeamentos

Há muitos tipos de dados do SQL Server que você pode mapear a CLR um ou mais tipos de dados. Também há muitos tipos de CLR que você pode mapear a um ou mais tipos SQL Server. Embora um mapeamento pode ser suportados por LINQ to SQL, não significa que os dois tipos mapeadas entre CLR e o SQL Server são uma correspondência perfeita com precisão, intervalo, e semântica. Alguns mapeamentos podem incluir diferenças em algumas ou todas essas dimensões. Você pode localizar detalhes sobre essas diferenças possíveis para as diversas possibilidades de mapeamento em Mapeamento do tipo SQL-CLR.

Tipos definidos pelo usuário

Tipos definidos pelo usuário de CLR são criados para ajudar a ponte entre o intervalo do sistema de tipos. Entretanto problemas surgem interessantes sobre o controle de versão do tipo. Uma alteração na versão do cliente não pode ser correspondida por uma alteração no tipo armazenado no servidor de base de dados. Uma alteração causa um outros tipos incompatíveis onde a semântica de tipo pode não corresponder e o intervalo de versão é provável ficar visível. Uma complicações adicionais ocorrem a hierarquias de herança refactored em sucessivas versões.

Semântica de expressão

Além de por pares a incompatibilidade entre CLR e tipos de base de dados, expressões adicionar complexidade a incompatibilidade. As incompatíveis na semântica de operador, na semântica de função, na conversão implícita de tipos, e as regras de precedência devem ser consideradas.

As seguintes subseções ilustram a incompatibilidade entre expressões aparentemente semelhantes. Talvez seja possível gerar as expressões SQL que são semanticamente equivalentes a uma expressão fornecida de CLR. No entanto, não é claro se semânticas as diferenças entre expressões aparentemente semelhantes são evidentes a um usuário de CLR, e portanto se as alterações que são necessárias para equivalências semântica estão se ou não. Isso é especialmente importante quando um assunto uma expressão é avaliada para um conjunto de valores. A visibilidade da diferença pode depender de dados e ser difícil de identificar durante a codificação e depuração.

Semântica nula

As expressões SQL fornecem lógica três avaliada para expressões booleanas. O resultado pode ser true, false, ou zero. Por outro lado, CLR especifica o resultado booleano duas avaliado para as comparações que envolvem valores nulos. Considere o seguinte código:

Nullable<int> i = null;
Nullable<int> j = null;
if (i == j)
{
    // This branch is executed.
}
Dim i? As Integer = Nothing
Dim j? As Integer = Nothing
If i = j Then
    '  This branch is executed.
End If
-- Assume col1 and col2 are integer columns with null values.
-- Assume that ANSI null behavior has not been explicitly
--  turned off.
Select …
From …
Where col1 = col2
-- Evaluates to null, not true and the corresponding row is not
--   selected.
-- To obtain matching behavior (i -> col1, j -> col2) change
--   the query to the following:
Select …
From …
Where
    col1 = col2
or (col1 is null and col2 is null)
-- (Visual Basic 'Nothing'.)

Um problema semelhante ocorre com a suposição sobre resultados duas avaliados.

if ((i == j) || (i != j)) // Redundant condition.
{
    // ...
}
If (i = j) Or (i <> j) Then ' Redundant condition.
    ' ...
End If
-- Assume col1 and col2 are nullable columns.
-- Assume that ANSI null behavior has not been explicitly
--   turned off.
Select …
From …
Where
    col1 = col2
or col1 != col2
-- Visual Basic: col1 <> col2.

-- Excludes the case where the boolean expression evaluates
--   to null. Therefore the where clause does not always
--   evaluate to true.

Em casos anteriores, você pode obter o comportamento equivalente em gerar o SQL, mas a conversão não pode exatamente refletir sua intenção.

LINQ to SQL não impõe semântica de comparação C# null ou Visual Basic nothing no SQL. Os operadores de comparação são convertidos sintaticamente para seus equivalentes SQL. Semânticas reflete a semântica do SQL como definida pelas configurações do servidor ou de conexão. Dois valores nulos são considerados configurações padrão inferiores desiguais SQL Server (embora você possa alterar as configurações para alterar a semântica). De qualquer forma, LINQ to SQL não considera configurações de servidor para a conversão da consulta.

Uma comparação com null literal (nothing) é convertido para a versão apropriada do SQL (is null ou is not null).

O valor de null (nothing) na ordenação é definido pelo SQL Server; LINQ to SQL não altera o agrupamento.

Conversão de tipos e promoção

O SQL suporta um conjunto rico de conversões implícitas em expressões. As expressões semelhantes em C# exigiriam uma conversão explícita. Por exemplo:

  • Nvarchar e os tipos de DateTime podem ser comparadas em SQL sem nenhuma conversões explícitas; C# requer conversão explícita.

  • Decimal é convertido implicitamente a DateTime no SQL. C# não permite uma conversão implícita.

Também, a precedência de tipo em Transact-SQL difere de precedência de tipo em C# porque o conjunto sendo a base de tipos é diferente. De fato, não há nenhuma relação clara do subconjunto/superconjunto entre as listas de precedência. Por exemplo, nvarchar comparar com varchar faz com que a conversão implícita da expressão de varchar a nvarchar. CLR não fornece nenhuma promoção equivalente.

Em casos simples, essas diferenças fazem com que as expressões de CLR com conversões sejam redundantes para uma expressão correspondente SQL. Mais importante, os resultados intermediários de uma expressão SQL podem ser implicitamente promovidos para um tipo que não tem contraparte exata em C#, e vice-versa. Total, os testes, depuração, e a validação dessas expressões adiciona a carga significativa no usuário.

Collation

Transact-SQL suporta ordenações explícitas como as anotações a cadeia de caracteres tipo. Essas ordenações determinam a validade de determinadas comparações. Por exemplo, comparar duas colunas com diferentes ordenações explícitas é um erro. O uso do tipo muito mais simples de cadeia de caracteres de CTS não causa esses erros. Considere o seguinte exemplo:

create table T2 (
    Col1 nvarchar(10),
    Col2      nvarchar(10) collate Latin_general_ci_as
)
class C
{
string s1;       // Map to T2.Col1.
string s2;       // Map to T2.Col2.

    void Compare()
    {
        if (s1 == s2) // This is correct.
        {
            // ...
        }
    }
}
Class C
    Dim s1 As String    ' Map to T2.Col1.
    Dim s2 As String    ' Map to T2.Col2.
    Sub Compare()
        If s1 = s2 Then ' This is correct.
            ' ...
        End If
    End Sub
End Class
Select …
From …
Where Col1 = Col2
-- Error, collation conflict.

Aplicado, o agrupamento da subcláusula cria um tipo restrito que não é substituível.

Da mesma forma, a ordem de classificação pode ser significativamente diferente entre sistemas de tipos. Essa diferença afeta a classificação dos resultados. Guid são classificadas em todos os 16 bytes pela ordem lexicographic (IComparable()), enquanto T-SQL compara GUIDs na seguinte ordem: (nó 10-15), relógio- segs. (8-9), Hora- alto (6-7), Hora- mid (4-5), Hora- baixo (0-3). Isso regras que foi feito no SQL 7.0 GUIDs gerado quando NT- tinha uma ordem de octeto. A abordagem de assegurou-se que GUIDs gerado no mesmo conjunto de nó viesse juntos em ordem sequencial de acordo com o carimbo de data/hora. A abordagem também é útil para compilar índices (inserções se tornam append em vez de IOs aleatório). A ordem scrambled posteriormente no Windows devido a interesses privacidade, mas o SQL deve manter compatibilidade. Uma solução alternativa é usar SqlGuid em vez de Guid.

Operador e diferenças de função

Os operadores e funções que são essencialmente comparáveis têm a semântica ligeiramente diferente. Por exemplo:

  • Especifica C# procura por um caminho menor a semântica com base na ordem lexicalmente de operandos para operadores lógicos && e ||. O SQL por outro lado é definido para consultas definir - com base e portanto fornece mais liberdade de otimizador para decidir a ordem de execução. Algumas implicações incluem o seguinte:

    • Tradução semanticamente equivalente exigiria "CASEWHENTHEN" construto no SQL para evitar reordenar a execução de operando.

    • Uma conversão fraca a operadores AND/OR pode causar erros inesperados se a expressão C# depende da avaliação o segundo operando sendo baseado no resultado da avaliação do primeiro operando.

  • Round() a função tem a semântica diferente em .NET Framework e em T-SQL.

  • Iniciar o índice para cadeias de caracteres é 0 em CLR mas 1 no SQL. Portanto, qualquer função que tem a conversão de índice das necessidades de índice.

  • O módulo de suporte de CLR (“% ") o operador para números de ponto flutuante mas SQL não.

  • O operador de Like efetivamente chamando as sobrecargas automático com base em conversões implícitas. Embora o operador de Like é definido para operar em tipos de cadeia de caracteres, a conversão implícita de tipos numéricos ou de tipos de DateTime permite esses tipos não-cadeia de caracteres a ser usado assim como com Like . Em CTS, as conversões implícitas comparáveis não existem. Portanto, as sobrecargas adicionais são necessárias.

    Observação

    Este comportamento do operador de Like se aplica a C# somente; a palavra-chave do Visual Basic Like é inalterado.

  • Overflow é sempre SQL fazer check-in mas tem que ser explicitamente especificado em C# (não em ) para evitar o wraparound. Colunas disponíveis C1, C2 e C3 inteiro, se C1+C2 é armazenado em C3 (atualização T ajustada C3 = C1 + C2).

    create table T3 (
        Col1      integer,
        Col2      integer
    )
    insert into T3 (col1, col2) values (2147483647, 5)
    -- Valid values: max integer value and 5.
    select * from T3 where col1 + col2 < 0
    -- Produces arithmetic overflow error.
    
// C# overflow in absence of explicit checks.
int i = Int32.MaxValue;
int j = 5;
if (i+j < 0) Console.WriteLine("Overflow!");
// This code prints the overflow message.
' Does not apply.
' Visual Basic overflow in absence of implicit check
' (turn off overflow checks in compiler options)
Dim I As Integer = Int32.MaxValue
Dim j As Integer = 5
If I + j < 0 Then
    ' This code prints the overflow message.
    Console.WriteLine("Overflow!")
End If
  • O SQL arredondamento executa aritmética simétrico quando usar arredondamento bancário do .NET Framework. Consulte o artigo 196652 da knowledgebase para obter detalhes adicionais.

  • Por padrão, para localidades comuns, as comparações de cadeia de caracteres não diferenciam maiúsculas de minúsculas no SQL. No Visual Basic e C#, diferenciam maiúsculas de minúsculas. Por exemplo, s == "Food" (s = "Food" em Visual Basic) e s == "Food" podem produzir resultados diferentes se s for food.

    -- Assume default US-English locale (case insensitive).
    create table T4 (
        Col1      nvarchar (256)
    )
    insert into T4 values ('Food')
    insert into T4 values ('FOOD')
    select * from T4 where Col1 = 'food'
    -- Both the rows are returned because of case-insensitive matching.
    
// C# equivalent on collections of Strings in place of nvarchars.
String[] strings = { "food", "FOOD" };
foreach (String s in strings)
{
    if (s == "food")
    {
        Console.WriteLine(s);
    }
}
// Only "food" is returned.
' Visual Basic equivalent on collections of Strings in place of
' nvarchars.
Dim strings() As String = {"food", "FOOD"}
For Each s As String In strings
    If s = "food" Then
        Console.WriteLine(s)
    End If
Next
' Only "food" is returned.
  • Funções de operadores aplicadas a fixos argumentos de tipo de caracteres de comprimento em SQL têm a semântica significativamente diferente do que os mesmos operadores/funções aplicados a CLR System.String. Isso pode também ser exibido como uma extensão do problema ausente de contrapartes discutido na seção sobre tipos.

    create table T4 (
        Col1      nchar(4)
    )
    Insert into T5(Col1) values ('21');
    Insert into T5(Col1) values ('1021');
    Select * from T5 where Col1 like '%1'
    -- Only the second row with Col1 = '1021' is returned.
    -- Not the first row!
    
    // Assume Like(String, String) method.
    string s = ""; // map to T4.Col1
    if (System.Data.Linq.SqlClient.SqlMethods.Like(s, "%1"))
    {
        Console.WriteLine(s);
    }
    // Expected to return true for both "21" and "1021"
    
    ' Assume Like(String, String) method.
    Dim s As String    ' Map to T4.Col1.
    If s Like (System.Data.Linq.SqlClient.SqlMethods.Like(s, "%1")) Then
        Console.WriteLine(s)
    End If
    ' Expected to return true for both "21" and "1021".
    

    Um problema semelhante ocorre com concatenação de cadeia de caracteres.

    create table T6 (
        Col1      nchar(4)
        Col2       nchar(4)
    )
    Insert into T6 values ('a', 'b');
    Select Col1+Col2 from T6
    -- Returns concatenation of padded strings "a   b   " and not "ab".
    

Em resumo, uma translação complicada pode ser necessária para expressões de CLR e os operadores/funções podem ser necessários para expor a funcionalidade SQL.

Conversão de tipo

Em C# e no SQL, os usuários podem subtituir a semântica padrão de expressões usando conversões explícitas de tipos (Cast e Convert). No entanto, expor esse recurso até o limite do sistema de tipos gerencie um dilema. Um SQL converte-se que fornecesse a semântica desejada não pode facilmente ser convertido para uma conversão correspondente C#. Por outro lado, uma conversão C# não pode diretamente ser convertida em um equivalente SQL convertido devido aos tipos incompatíveis, a contrapartes ausente, e para hierarquias diferentes de precedência de tipo. Há uma escolha entre a exposição de incompatibilidade do sistema de tipos e poder significativa perdedora da expressão.

Em outros casos, a conversão de tipo não pode ser necessária em qualquer domínio para validação de uma expressão mas pode ser necessária para certificar-se de que um mapeamento padrão não é aplicado corretamente para a expressão.

-- Example from "Non-default Mapping" section extended
create table T5 (
    Col1      nvarchar(10),
    Col2      nvarchar(10)
)
Insert into T5(col1, col2) values ('3', '2');
class C
{
    int x;        // Map to T5.Col1.
    int y;        // Map to T5.Col2.

    void Casting()
    {
        // Intended predicate.
        if (x + y > 4)
        {
            // valid for the data above
        }
    }
}
Class C
    Dim x As Integer        ' Map to T5.Col1.
    Dim y As Integer        ' Map to T5.Col2.

    Sub Casting()
        ' Intended predicate.
        If (x + y) > 4 Then
            ' Valid for the data above.
        End If
    End Sub
End Class
Select *
From T5
Where Col1 + Col2 > 4
-- "Col1 + Col2" expr evaluates to '32'

Problemas de desempenho

Esclarecer algumas diferenças de tipo SQL SERVIDOR- CLR pode resut em um decréscimo de desempenho ao cruzar-se entre CLR e sistemas de tipos do SQL Server. Exemplos de cenários que afetam o desempenho incluem o seguinte:

  • Forçado a ordem de classificação para lógico e/ou operadores

  • Gerar o SQL para aplicar a ordem de classificação de predicado restringe a capacidade de otimizador SQL.

  • Conversões de tipos, se introduzido por um compilador de CLR ou por uma implementação objeto relacional de consulta, podem reduzir o uso de índice.

    Por exemplo,

    -- Table DDL
    create table T5 (
        Col1      varchar(100)
    )
    
    class C5
    {
        string s;        // Map to T5.Col1.
    }
    
    Class C5
        Dim s As String ' Map to T5.Col1.
    End Class
    

    Considere a conversão de expressão (s = SOME_STRING_CONSTANT).

    -- Corresponding part of SQL where clause
    Where …
    Col1 = SOME_STRING_CONSTANT
    -- This expression is of the form <varchar> = <nvarchar>.
    -- Hence SQL introduces a conversion from varchar to nvarchar,
    --   resulting in
    Where …
    Convert(nvarchar(100), Col1) = SOME_STRING_CONSTANT
    -- Cannot use the index for column Col1 for some implementations.
    

Além de diferenças semânticas, é importante considerar impactos o desempenho ao cruzar-se entre o SQL Server e sistemas de tipos de CLR. Para grandes conjuntos de dados, tais problemas de desempenho podem determinar se um aplicativo está deployable.

Confira também