Сравнения 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.