Null 比较

数据源中的 null 值指示未知的值。 在 LINQ to Entities 查询中,可以检查 Null 值以便仅对具有有效(非 Null)数据的行执行特定的计算或比较。 但是,CLR null 语义可能与数据源的 null 语义不同。 大多数数据库使用某个版本的三值逻辑处理 null 比较。 即,对 null 值的比较不会计算为 truefalse,而是计算为 unknown。 通常这是 ANSI null 值的实现,但情况并非总是如此。

在 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  

这与 CLR 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 : {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 常量进行比较或两个 null 常量进行比较,则会展现 CLR null 语义。 如果数据源中具有 null 值的两个列进行比较,则会展现存储 null 语义。 键选择器出现在许多分组和排序标准查询运算符(如 GroupBy)中,用于选择对查询结果进行排序或分组所依据的键。

Null 对象上的 Null 属性

在实体框架中,Null 对象的属性为 Null。 当尝试引用 CLR 中的 null 对象的属性时,会收到 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 的值类型。

请参阅