数据源中的 null 值指示未知的值。在 LINQ to Entities 查询中,可以检查 null 值以便仅对具有有效(非 null)数据的行执行特定的计算或比较。但是,CLR null 语义可能与数据源的 null 语义不同。大多数数据库使用某个版本的三值逻辑处理 null 比较。即,对 null 值的比较不会计算为 true 或 false,而是计算为 unknown。通常这是 ANSI null 值的实现,但情况并非总是如此。
在 SQL Server 中,null 等于 null 比较默认返回 null 值。在下面的示例中,Region 为 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
这与 CLR null 语义有很大差异,在 CLR null 语义中 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 常量进行比较或两个 null 常量进行比较,则会展现 CLR null 语义。如果数据源中具有 null 值的两个列进行比较,则会展现存储 null 语义。键选择器出现在许多分组和排序标准查询运算符(如 GroupBy)中,用于选择对查询结果进行排序或分组所依据的键。
Null 对象上的 Null 属性
在 实体框架 中,null 对象的属性为 null。当尝试引用 CLR 中的 null 对象的属性时,会收到 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);
}