Сравнения со значением Null
Значение null в источнике данных указывает на то, что это значение неизвестно. В запросах LINQ to Entities можно реализовать проверку наличия значений NULL так, чтобы определенные вычисления или сравнения выполнялись только для строк с допустимыми данными, не содержащими значений NULL. Впрочем, null-семантика среды CLR может отличаться от null-семантики источника данных. В большинстве баз данных для выполнения сравнений со значением Null используется трехзначная логика. Иначе говоря, результат сравнения со значением Null не дает ни true, ни false; в результате получается значение unknown. Часто речь идет о реализациях Null ANSI, но так бывает не всегда.
По умолчанию в SQL Server сравнение «Null равняется Null» возвращает значение Null. В следующем примере строки, где параметр «Регион» имеет значение Null, исключаются из результирующего набора и инструкция Transact-SQL возвращает 0 строк.
-- Find orders and customers with no regions.
SELECT a.[CustomerID]
FROM [Northwind].[dbo].[Customers] a
JOIN [Northwind].[dbo].[Orders] b ON a.Region = b.ShipRegion
WHERE a.Region IS Null
Это совсем не похоже на Null-семантику среды CRL, где сравнение «Null равняется Null» возвращает значение True.
Следующий запрос LINQ выражается в среде CLR, но выполняется в источнике данных. Гарантии того, что семантика CLR будет действительна для среды источника данных, не существует, поэтому предполагаемое поведение непредсказуемо.
Using NwEntities As New NorthwindEntities()
Dim customers As ObjectQuery(Of Customers) = NwEntities.Customers
Dim orders As ObjectQuery(Of Orders) = NwEntities.Orders
Dim query = _
From c In customers _
Join o In orders On c.Region Equals o.ShipRegion _
Where c.Region = Nothing _
Select c.CustomerID
For Each customerID In query
Console.WriteLine("Customer ID: ", customerID)
Next
End Using
using (NorthwindEntities NwEntities = new NorthwindEntities())
{
ObjectQuery<Customers> customers = NwEntities.Customers;
ObjectQuery<Orders> orders = NwEntities.Orders;
IQueryable<string> query = from c in customers
join o in orders on c.Region equals o.ShipRegion
where c.Region == null
select c.CustomerID;
foreach (string customerID in query)
{
Console.WriteLine("Customer ID: {0}", customerID);
}
}
Селекторы ключей
Селектор ключа — функция, используемая в стандартных операторах запросов для извлечения ключа из элемента. В функции селектора элемента может быть выполнено сравнение выражения с константой. 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 AWEntities As New AdventureWorksEntities()
Dim dt As DateTime = New DateTime()
Dim query = AWEntities.Product _
.Where(Function(p) _
((DirectCast(p, NewProduct)).Introduced > dt)) _
.Select(Function(x) x)
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
DateTime dt = new DateTime();
var query = AWEntities.Product
.Where(p => (p as NewProduct).Introduced > dt)
.Select(x => x);
}