Confronti Null

Un valore null nell'origine dati indica che il valore è sconosciuto. Nella query LINQ to Entities è possibile verificare la presenza di valori Null in modo che alcuni calcoli o confronti vengano eseguiti solo sulle righe che includono dati validi, ovvero non NULL. Tuttavia, la semantica dei valori Null di CLR può differire da quella dell'origine dati. La maggior parte dei database usa una versione della logica con tre valori per la gestione dei confronti di valori Null. In altre parole, il confronto di un valore Null non restituisce true o false, ma unknown. Spesso, ma non sempre, si tratta di un'implementazione di valori Null ANSI.

Per impostazione predefinita, in SQL Server il confronto tra valori Null con il metodo Equals restituisce un valore Null. Nell'esempio seguente, le righe in cui ShipDate è null sono escluse dal set di risultati e l'istruzione Transact-SQL restituisce 0 righe.

-- 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  

Tale semantica è molto diversa da quella dei valori Null di CLR, in cui dal confronto tra valori Null con il metodo Equals viene restituito True.

La query LINQ seguente è espressa in CLR, ma viene eseguita nell'origine dati. Poiché non esiste alcuna garanzia che venga rispettata la semantica CLR nell'origine dati, il comportamento previsto è indeterminato.

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

Selettori di chiave

Un selettore di chiave è una funzione usata negli operatori di query standard per estrarre una chiave da un elemento. Nella funzione del selettore di chiave un'espressione può essere confrontata con una costante. La semantica dei valori Null di CLR viene usata in caso di confronto tra un'espressione e una costante Null o tra due costanti Null. La semantica dei valori Null dell'archivio viene usata se vengono confrontate due colonne con valori Null nell'origine dati. I selettori di chiave sono disponibili in molti operatori di query standard per il raggruppamento e l'ordinamento, come GroupBy e vengono usati per selezionare le chiavi in base alle quali ordinare o raggruppare i risultati delle query.

Proprietà Null in un oggetto Null

In Entity Framework le proprietà di un oggetto Null sono Null. Quando si tenta di fare riferimento a una proprietà di un oggetto Null in CLR, viene ricevuto un oggetto NullReferenceException. Quando una query LINQ include una proprietà di un oggetto Null, è possibile che si verifichi un comportamento incoerente.

Nella query seguente, ad esempio, viene eseguito il cast a NewProduct nel livello dell'albero dei comandi. Ciò potrebbe rendere la proprietà Introduced uguale a Null. Se nel database sono stati definiti confronti di valori Null in modo che il risultato del confronto di DateTime sia True, la riga verrà inclusa.

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

Passaggio di raccolte null a funzioni di aggregazione

In LINQ to Entities, quando si passa una raccolta che supporta IQueryable a una funzione di aggregazione, le operazioni di aggregazione vengono eseguite a livello di database. Vi potrebbero essere delle differenze tra i risultati di una query eseguita in memoria e una query eseguita a livello di database. Se non vengono trovate corrispondenze, la query in memoria restituisce zero. A livello di database, la stessa query restituisce null. Se un null valore viene passato a una funzione di aggregazione LINQ, verrà generata un'eccezione. Per accettare valori possibili null, eseguire il cast dei tipi e delle proprietà dei tipi che ricevono i risultati della query a tipi valore nullable.

Vedi anche