Conversão padrão de operador de consulta

O LINQ to SQL converte operadores de consulta padrão em comandos SQL. O processador de consulta do banco de dados determina a semântica de execução de conversão SQL.

Os Operadores de Consulta Padrão Standard são definidos em relação a sequências. Uma sequência é ordenada e depende da identidade de referência de cada elemento da sequência. Para obter mais informações, confira Visão geral de operadores de consulta padrão (C#) ou Visão geral de operadores de consulta padrão (Visual Basic).

O SQL trata principalmente conjuntos de valores não ordenados. A ordenação normalmente é indicada explicitamente, uma operação de pós-processamento que é aplicada ao resultado final de uma consulta em vez de aos resultados intermediários. A identidade é definida por valores. Por esta razão, as consultas SQL são supostas a manipular multisets (recipientes) em vez de conjuntos.

Os parágrafos a seguir descrevem as diferenças entre os operadores de consulta padrão e sua conversão SQL do provedor SQL Server para LINQ to SQL.

Suporte de operador

Concat

O método Concat é definido para multisets ordenados onde a ordem do receptor e a ordem do argumento são as mesmas. O Concat funciona como UNION ALL sobre os multisets seguido pela ordem comum.

A etapa final é ordenar no SQL antes que os resultados sejam produzidos. O Concat não preserva a ordem de seus argumentos. Para garantir a ordem apropriada, você deve ordenar explicitamente os resultados de Concat.

Intersect, Except, Union

Os métodos Intersect e Except são bem-definidos somente em conjuntos. A semântica de multisets é indefinida.

O método Union é definido para multisets como a concatenação não ordenada dos multisets (efetivamente o resultado da cláusula UNION ALL no SQL).

Take, Skip

Os métodos Take e Skip são bem-definidos somente em relação a conjuntos ordenados. A semântica de conjuntos ou de multisets não ordenados é indefinida.

Observação

Take e Skip possuem certas limitações quando são usados em consultas no SQL Server 2000. Para obter mais informações, confira a entrada "Exceções Skip e Take no SQL Server 2000" em Solucionar problemas.

Devido às restrições da ordenação no SQL, o LINQ to SQL tenta mover a ordenação do argumento desses métodos para o resultado do método. Por exemplo, considere a seguinte consulta LINQ to SQL:

var custQuery =
    (from cust in db.Customers
    where cust.City == "London"
    orderby cust.CustomerID
    select cust).Skip(1).Take(1);
Dim custQuery = _
    From cust In db.Customers _
    Where cust.City = "London" _
    Order By cust.CustomerID _
    Select cust Skip 1 Take 1

O SQL gerado para esse código move a ordenação para o final, da seguinte maneira:

SELECT TOP 1 [t0].[CustomerID], [t0].[CompanyName],
FROM [Customers] AS [t0]
WHERE (NOT (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM (
        SELECT TOP 1 [t1].[CustomerID]
        FROM [Customers] AS [t1]
        WHERE [t1].[City] = @p0
        ORDER BY [t1].[CustomerID]
        ) AS [t2]
    WHERE [t0].[CustomerID] = [t2].[CustomerID]
    ))) AND ([t0].[City] = @p1)
ORDER BY [t0].[CustomerID]

Torna-se óbvio que qualquer ordenação especificada precisa ser consistente quando Take e Skip são encadeados juntos. Caso contrário, os resultados serão indefinidos.

Take e Skip são definidos para argumentos inteiros não negativos, argumentos integrais constantes baseados na especificação de operador de consulta padrão.

Operadores sem a conversão

Os seguintes métodos não são convertidos pelo LINQ to SQL. O motivo mais comum é a diferença entre multisets e sequências não ordenados.

Operadores Fundamento
TakeWhile, SkipWhile Consultas SQL operam em multisets, não em sequências. ORDER BY deve ser a última cláusula aplicada aos resultados. Por esse motivo, não há nenhuma conversão de finalidade geral para esses dois métodos.
Reverse A conversão desse método é possível para um conjunto ordenado, mas, no momento, não é convertido pelo LINQ to SQL.
Last, LastOrDefault A conversão desses métodos é possível para um conjunto ordenado, mas, no momento, não são convertidos pelo LINQ to SQL.
ElementAt, ElementAtOrDefault As consultas SQL operam em multisets, não em sequências indexáveis.
DefaultIfEmpty (sobrecarga com arg padrão) Em geral, um valor padrão não pode ser especificado para um tuple arbitrário. Valores nulos para tuples são possíveis em alguns casos com junções externas.

Conversão de expressão

Semântica de nulo

O LINQ to SQL não impõe semântica de comparação de nulo no SQL. Os operadores de comparação são convertidos sintaticamente para seus equivalentes SQL. Por esse motivo, a semântica reflete a semântica do SQL definida pelas configurações do servidor ou da conexão. Por exemplo, dois valores nulos são considerados diferentes sob as configurações padrão do SQL Server, mas você pode alterar as configurações para alterar a semântica. O LINQ to SQL não considera as configurações do servidor quando converte consultas.

Uma comparação com o nulo literal é convertida na versão apropriada do SQL (is null ou is not null).

O valor de null na ordenação é definido pelo SQL Server. O LINQ to SQL não altera a ordenação.

Agregações

O método de agregação do operador de consulta padrão Sum avalia uma sequência vazia ou uma sequência que contém somente nulos como zero. No LINQ to SQL, a semântica do SQL é mantida inalterada, e Sum avalia uma sequência vazia ou uma sequência que contém somente null e não como zero.

As limitações do SQL em resultados intermediários se aplicam às agregações no LINQ to SQL. O Sum de quantidades inteiras de 32 bits não foi calculada usando resultados de 64 bits. Poderá ocorrer um estouro de uma conversão LINQ to SQL do Sum, mesmo se a implementação do operador de consulta padrão não causar um estouro da sequência correspondente na memória.

Da mesma forma, a conversão LINQ to SQL do Average de valores inteiros é computada como um integer, e não como um double.

Argumentos de entidade

O LINQ to SQL permite que tipos de entidade sejam usados nos métodos OrderBy e GroupBy. Na conversão desses operadores, o uso de um argumento de um tipo é considerado ser o equivalente a especificar todos os membros desse tipo. Por exemplo, o código a seguir é equivalente:

db.Customers.GroupBy(c => c);
db.Customers.GroupBy(c => new { c.CustomerID, c.ContactName });
db.Customers.GroupBy(Function(c) c)
db.Customers.GroupBy(Function(c) New With {c.CustomerID, _
    c.ContactName})

Argumentos equitativos/comparáveis

A igualdade dos argumentos é necessária na implementação dos seguintes métodos:

O LINQ to SQL dá suporte à igualdade e à comparação para argumentos simples, mas não para argumentos que são ou contêm sequências. Um argumento simples é um tipo que pode ser mapeado para uma linha SQL. Uma projeção de um ou mais tipos de entidade, que podem ser determinados estaticamente por não conterem uma sequência, é considerada um argumento simples.

Veja abaixo exemplos de argumentos simples:

db.Customers.Select(c => c);
db.Customers.Select(c => new { c.CustomerID, c.City });
db.Orders.Select(o => new { o.OrderID, o.Customer.City });
db.Orders.Select(o => new { o.OrderID, o.Customer });	
db.Customers.Select(Function(c) c)
db.Customers.Select(Function(c) New With {c.CustomerID, c.City})
db.Orders.Select(Function(o) New With {o.OrderID, o.Customer.City})
db.Orders.Select(Function(o) New With {o.OrderID, o.Customer})

Veja abaixo exemplos de argumentos que não são simples (hierárquicos):

// In the following line, c.Orders is a sequence.
db.Customers.Select(c => new { c.CustomerID, c.Orders });
// In the following line, the result has a sequence.
db.Customers.GroupBy(c => c.City);
' In the following line, c.Orders is a sequence.
db.Customers.Select(Function(c) New With {c.CustomerID, c.Orders})
' In the following line, the result has a sequence.
db.Customers.GroupBy(Function(c) c.City)

Conversão de função do Visual Basic

As seguintes funções auxiliares que são usadas pelo compilador Visual Basic são convertidas em operadores e funções SQL correspondentes:

  • CompareString

  • DateTime.Compare

  • Decimal.Compare

  • IIf (in Microsoft.VisualBasic.Interaction)

Métodos de conversão:

  • ToBoolean
  • ToSByte
  • ToByte
  • ToChar
  • ToCharArrayRankOne
  • ToDate
  • ToDecimal
  • ToDouble
  • ToInteger
  • ToUInteger
  • ToLong
  • ToULong
  • ToShort
  • ToUShort
  • ToSingle
  • ToString

Suporte à herança

Restrições de mapeamento de herança

Para obter mais informações, confira Como mapear hierarquias de herança.

Herança em consultas

As conversões do C# são suportadas apenas na projeção. Conversões que são usadas em outro lugar não são convertidas e são ignoradas. Com exceção dos nomes de funções SQL, o SQL realmente executa apenas o equivalente do Convert do CLR (Common Language Runtime). Isto é, o SQL pode alterar o valor de um tipo para outro. Não há nenhum equivalente de conversão do CLR porque não há nenhum conceito de reinterpretação dos mesmos bits como os de outro tipo. É por isso que uma conversão C# funciona apenas localmente. Ela não funciona remotamente.

Os operadores is e as e o método GetType não são restritos ao operador Select. Podem ser usados em outros operadores de consulta também.

Suporte do SQL Server 2008

A partir do .NET Framework 3.5 SP1, o LINQ to SQL dá suporte ao mapeamento para novos tipos de data e hora introduzidos com o SQL Server 2008. Mas, há algumas limitações aos operadores de consulta do LINQ to SQL que você pode usar ao operar com valores mapeados para esses novos tipos.

Operadores de consulta não suportados

Os seguintes operadores de consulta não são suportados em valores mapeados para novos tipos de data e hora do SQL Server: DATETIME2, DATE, TIME e DATETIMEOFFSET.

  • Aggregate

  • Average

  • LastOrDefault

  • OfType

  • Sum

Para obter mais informações sobre o mapeamento para esses tipos de data e hora do SQL Server, confira Mapeamento de tipo SQL-CLR.

Suporte do SQL Server 2005

O LINQ to SQL não dá suporte aos seguintes recursos do SQL Server 2005:

  • Procedimentos armazenados escritos para SQL CLR.

  • Tipo definido pelo usuário.

  • Recursos de consulta XML.

Suporte do SQL Server 2000

As seguintes limitações de SQL Server 2000 (em comparação com o Microsoft SQL Server 2005) afetam o suporte do LINQ to SQL.

Operadores Cross Apply e Outer Apply

Esses operadores não estão disponíveis no SQL Server 2000. O LINQ to SQL tenta uma série de reescritas para substituí-los com junções apropriadas.

O Cross Apply e o Outer Apply são gerados para navegações de relacionamento. O conjunto de consultas para as quais essas reescritas são possíveis não é bem definido. Por esse motivo, o conjunto mínimo de consultas que é suportado para o SQL Server 2000 é o conjunto que não envolve a navegação de relacionamento.

text/ntext

Os tipos de dados text / ntext não podem ser usados em determinadas operações de consulta em varchar(max) / nvarchar(max), que são suportadas pelo Microsoft SQL Server 2005.

Nenhuma resolução está disponível para essa limitação. Especificamente, você não pode usar Distinct() em resultados que contêm membros que são mapeados para colunas text ou ntext.

Comportamento disparado por consultas aninhadas

O associador do SQL Server 2000 (por meio do SP4) tem algumas idiossincrasias que são disparadas por consultas aninhadas. O conjunto de consultas SQL que dispara essas idiossincrasias não é bem definido. Por esse motivo, você não pode definir o conjunto de consultas do LINQ to SQL que podem provocar exceções do SQL Server.

Operadores Skip e Take

Take e Skip possuem certas limitações quando são usados em consultas no SQL Server 2000. Para obter mais informações, confira a entrada "Exceções Skip e Take no SQL Server 2000" em Solucionar problemas.

Materialização de objetos

A materialização cria objetos CLR das linhas que são retornadas por uma ou mais consultas SQL.

  • As chamadas a seguir são executadas localmente como parte da materialização:

    • Construtores

    • Métodos ToString em projeções

    • Conversões de tipos em projeções

  • Os métodos que seguem o método AsEnumerable são executados localmente. Esse método não provoca uma execução imediata.

  • Você pode usar struct como o tipo de retorno de um resultado de consulta ou como um membro do tipo de resultado. As entidades precisam ser classes. Tipos anônimos são materializados como instâncias de classe, mas structs nomeados (não entidades) podem ser usados na projeção.

  • Um membro do tipo do retorno de um resultado de consulta pode ser do tipo IQueryable<T>. É materializado como uma coleção local.

  • Os métodos a seguir provocam a materialização imediata da sequência na qual os métodos são aplicados:

Confira também