Сравнения NULL

Значение null в источнике данных указывает на то, что это значение неизвестно. В запросах LINQ to Entity можно проверка для значений NULL, чтобы определенные вычисления или сравнения выполнялись только в строках, имеющих допустимые или непустые данные. Впрочем, null-семантика среды CLR может отличаться от null-семантики источника данных. В большинстве баз данных для выполнения сравнений со значением Null используется трехзначная логика. То есть сравнение со значением NULL не вычисляется true или falseвычисляется.unknown Часто речь идет о реализациях Null ANSI, но так бывает не всегда.

По умолчанию в SQL Server сравнение «Null равняется Null» возвращает значение Null. В следующем примере строки, в которых ShipDate значение NULL, исключены из результирующих наборов, а инструкция Transact-SQL вернет 0 строк.

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

Это совсем не похоже на Null-семантику среды CRL, где сравнение «Null равняется Null» возвращает значение True.

Следующий запрос LINQ выражается в среде CLR, но выполняется в источнике данных. Гарантии того, что семантика CLR будет действительна для среды источника данных, не существует, поэтому предполагаемое поведение непредсказуемо.

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

Селекторы ключей

Селектор ключей — это функция, используемая в стандартных операторах запросов для извлечения ключа из элемента. В функции селектора элемента может быть выполнено сравнение выражения с константой. Null-семантика CLR проявляется в тех случаях, когда выражение сравнивается с константой, имеющей значение Null, или когда сравниваются две константы со значениями Null. Null-семантика хранилищ проявляется в тех случаях, когда сравниваются два столбца источника данных со значениями Null. Селекторы ключей входят в состав многих используемых в запросах стандартных операторов группирования и упорядочивания, например GroupBy, и применяются для выделения ключей, по которым будет осуществляться упорядочение или группирование результатов запроса.

Свойство Null объекта Null

В Entity Framework свойства объекта NULL имеют значение NULL. При попытке обратиться к свойству объекта Null в среде CLR пользователь получает исключение NullReferenceException. Когда в LINQ-запросе задействовано свойство объекта Null, может быть получен несогласованный результат.

Так, в следующем примере приведение к типу NewProduct осуществляется на уровне дерева команд. В результате может получиться так, что свойство Introduced будет иметь значение Null. Если определенные в базе данных сравнения со значением Null таковы, что сравнение с DateTime дает значение True, то соответствующая строка будет включена.

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

Передача коллекций значений NULL в статистические функции

В LINQ to Entities при передаче коллекции, поддерживающей IQueryable агрегатную функцию, агрегатные операции выполняются в базе данных. В результатах запроса, выполняемого в памяти, могут быть различия, а также запрос, который был выполнен в базе данных. При запросе в памяти, если совпадений нет, запрос возвращает ноль. В базе данных тот же самый запрос возвращает null. null Если значение передается в агрегатную функцию LINQ, создается исключение. Чтобы принять возможные null значения, приведение типов и свойств типов, получающих результаты запроса, к типам значений, допускающим значение NULL.

См. также