Null 比较

null数据源中的值指示该值未知。 在 LINQ to Entities 查询中,可以检查空值,这样某些计算或比较仅针对包含完整数据的行进行。 但是,CLR null 语义可能与数据源的 null 语义不同。 大多数数据库使用三值逻辑版本来处理 null 比较。 也就是说,与 null 值比较的结果不是 true,也不是 false,而是 unknown。 通常,这是 ANSI null 的实现,但情况并不总是如此。

默认情况下,在 SQL Server 中,null 等于 null 比较返回 null 值。 在以下示例中,从结果集中排除 null 的 ShipDate 行,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 : {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 属性

在 Entity Framework 中,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 的集合时,聚合操作会在数据库中执行。 在内存中执行的查询的结果和在数据库中执行的查询的结果可能存在差异。 如果内存中查询没有匹配项,查询将返回零。 在数据库中,相同的查询返回 nullnull如果将值传递给 LINQ 聚合函数,将引发异常。 若要接受可能的 null 值,请将类型和接收查询结果的类型的属性强制转换为可以为 NULL 的值类型。

另请参阅