Comparações nulas

Um valor null na fonte de dados indica que o valor é desconhecido. Nas consultas do LINQ to Entities, você pode procurar valores nulos de modo que determinados cálculos ou comparações só sejam executados nas linhas que têm dados válidos ou não nulos. A semântica nula do CLR, no entanto, pode diferir da semântica nula da fonte de dados. A maioria dos bancos de dados usa uma versão da lógica de três valores para manipular comparações nulas. Ou seja, uma comparação com um valor nulo não é avaliada como true ou false; ela é avaliada como unknown. Geralmente, essa é uma implementação de valores nulos ANSI, mas isso nem sempre acontece.

Por padrão, no SQL Server, a comparação nulo igual a nulo retorna um valor nulo. No exemplo a seguir, as linhas em que ShipDate é null serão excluídas do conjunto de resultados, e a instrução Transact-SQL retornará 0 linhas.

-- Find order details and orders with no ship date.  
SELECT h.SalesOrderID  
FROM Sales.SalesOrderHeader h  
JOIN Sales.SalesOrderDetail o ON o.SalesOrderID = h.SalesOrderID  
WHERE h.ShipDate IS Null  

Isso é muito diferente de semântica nula do CLR, em que a comparação de null igual a null retorna true.

A seguinte consulta LINQ é expressa no CLR, mas é executada na fonte de dados. Como não há nenhuma garantia de que a semântica do CLR será respeitada na fonte de dados, o comportamento esperado será indeterminado.

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    ObjectSet<SalesOrderHeader> orders = context.SalesOrderHeaders;
    ObjectSet<SalesOrderDetail> details = context.SalesOrderDetails;

    var query =
        from order in orders
        join detail in details
        on order.SalesOrderID
        equals detail.SalesOrderID
        where order.ShipDate == null
        select order.SalesOrderID;

    foreach (var OrderID in query)
    {
        Console.WriteLine("OrderID : {0}", OrderID);
    }
}
Using context As New AdventureWorksEntities()

    Dim orders As ObjectSet(Of SalesOrderHeader) = context.SalesOrderHeaders
    Dim details As ObjectSet(Of SalesOrderDetail) = context.SalesOrderDetails

    Dim query = _
        From order In orders _
        Join detail In details _
        On order.SalesOrderID _
        Equals detail.SalesOrderID _
        Where order.ShipDate = Nothing
        Select order.SalesOrderID


    For Each orderID In query
        Console.WriteLine("OrderID: {0} ", orderID)
    Next
End Using

Seletores de chave

Um seletor de chave é uma função usada nos operadores de consulta padrão para extrair uma chave de um elemento. Na função do seletor de chave, uma expressão pode ser comparada a uma constante. A semântica nula do CLR será exibida se uma expressão for comparada com uma constante nula ou se duas constantes nulas forem comparadas. As semânticas nulas de armazenamento serão exibidas se duas colunas com valores nulos na fonte de dados forem comparadas. Os seletores de chave são encontrados em muitos operadores de consulta padrão de agrupamento e ordenação, como GroupBy, e usados para selecionar as chaves que servirão de base para a ordenação ou o agrupamento do resultados da consulta.

Propriedade nula em um objeto nulo

No Entity Framework, as propriedades de um objeto nulo são nulas. Ao tentar fazer referência à propriedade de um objeto nulo no CLR, você receberá NullReferenceException. Quando uma consulta LINQ envolver uma propriedade de um objeto nulo, o resultado possivelmente será um comportamento inconsistente.

Por exemplo, na consulta a seguir, a conversão em NewProduct é feita na camada da árvore de comandos, que pode resultar na propriedade Introduced que está sendo nula. Se o banco de dados tiver definido comparações nulas, como a comparação DateTime que é avaliada como true, a linha será incluída.

using (AdventureWorksEntities context = new AdventureWorksEntities())
{

    DateTime dt = new DateTime();
    var query = context.Products
        .Where(p => (p as NewProduct).Introduced > dt)
        .Select(x => x);
}
Using context As New AdventureWorksEntities()
    Dim dt As DateTime = New DateTime()
    Dim query = context.Products _
        .Where(Function(p) _
            ((DirectCast(p, NewProduct)).Introduced > dt)) _
        .Select(Function(x) x)
End Using

Passando coleções nulas para funções agregadas

No LINQ to Entities, quando você passa uma coleção que oferece suporte a IQueryable para uma função agregada, as operações agregadas são executadas no banco de dados. Talvez haja diferenças nos resultados de uma consulta executada na memória e de uma consulta executada no banco de dados. Com uma consulta de memória, se não houver nenhuma correspondência, a consulta retornará zero. No banco de dados, a mesma consulta retorna null. Se um valor null for passado para uma função agregada LINQ, uma exceção será lançada. Para aceitar os valores null possíveis, converta os tipos e as propriedades dos tipos que recebem resultados de consulta em tipos que permitem valores nulos.

Confira também