Сравнения значений NULL
Значение null
в источнике данных указывает, что значение неизвестно. В запросах LINQ to Entities можно проверить значения null, чтобы определенные вычисления или сравнения выполнялись только в строках с корректными или ненулевыми данными. Однако семантика CLR null может отличаться от семантики NULL источника данных. Большинство баз данных используют версию трехзначной логики для обработки сравнения значений NULL. То есть сравнение со значением NULL не оценивается как значение true
или false
, оно оценивается как значение unknown
. Часто это реализация ANSI NULL, но это не всегда так.
По умолчанию в SQL Server сравнение null-equals-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
Это очень отличается от семантики CLR NULL, где сравнение 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 : {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
Селекторы ключей
Выборка ключа — это функция, используемая в стандартных операторах запросов для извлечения ключа из элемента. В функции селектора ключей выражение можно сравнить с константой. Семантика CLR null отображается, если выражение сравнивается с константой NULL или если сравниваются две константы NULL. Если в источнике данных сравниваются два столбца с null-значениями, проявляется семантика null. Селекторы ключей находятся во многих стандартных операторах запросов группирования и упорядочивания, таких как GroupBy, и используются для выбора ключей, по которым можно упорядочить или сгруппировать результаты запроса.
Свойство NULL на объекте NULL
В Entity Framework свойства объекта NULL имеют значение NULL. При попытке ссылаться на свойство null-объекта в среде CLR вы получите NullReferenceException. Если запрос LINQ включает свойство пустого объекта, это может привести к несогласованности поведения.
Например, в следующем запросе приведение к NewProduct
выполняется на уровне дерева команд, что может привести к значению null свойства Introduced
. Если база данных определила сравнения NULL таким образом, что сравнение DateTime оценивается как истинное, строка будет включена.
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
Передача пустых коллекций в агрегатные функции
При передаче коллекции, поддерживающей IQueryable
в агрегатную функцию, агрегатные операции выполняются в базе данных в LINQ to Entities. В результатах запроса, выполняемого в памяти, и запроса, выполненного в базе данных, могут быть различия. При запросе в памяти, если совпадений нет, запрос возвращает ноль. В базе данных тот же запрос возвращает null
. Если значение null
передано в агрегатную функцию LINQ, будет выброшено исключение. Чтобы принять возможные значения null
, необходимо привести типы и свойства типов, которые получают результаты запросов, к допускающим NULL типам значений.
См. также
- выражения в запросах LINQ to Entities