.net 如何在实体框架中查询空值?

.net 如何在实体框架中查询空值?,.net,entity-framework,ado.net,.net,Entity Framework,Ado.net,我想执行这样的查询 var result = from entry in table where entry.something == null select entry; 并获取生成的为空的 编辑: 在前两个答案之后,我觉得有必要澄清我使用的是实体框架 而不是Linq到SQL。object.Equals()方法在EF中似乎不起作用 第2号编辑: 上述查询按预期工作。它正确地生成为NULL。然而,我的生产

我想执行这样的查询

   var result = from entry in table
                     where entry.something == null
                     select entry;
并获取生成的
为空的

编辑: 在前两个答案之后,我觉得有必要澄清我使用的是实体框架 而不是Linq到SQL。object.Equals()方法在EF中似乎不起作用

第2号编辑: 上述查询按预期工作。它正确地生成
为NULL
。然而,我的生产代码是

value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;
var value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;
生成的SQL是
something=@p@p=NULL
。似乎EF正确地翻译了常量表达式,但如果涉及变量,它会将其视为正常比较。实际上是有道理的。我将结束这个问题

var result = from entry in table
             where entry.something.Equals(null)
             select entry;

MSDN Reference

要处理空比较,请使用
Object.Equals()
而不是
=


选中此项

如果它是可为空的类型,是否可以尝试使用HasValue属性

var result = from entry in table
                 where !entry.something.HasValue
                 select entry;

没有任何EF测试在这里虽然。。。只是一个建议=)

看来Linq2Sql也有这个“问题”。由于ANSI NULLs是开还是关,这种行为似乎有一个合理的原因,但它让人难以理解为什么一个直接的“==null”实际上会如您所期望的那样工作

var result = from entry in table
                     where entry.something == null
                     select entry;
上述查询按预期工作。它正确地生成空值。然而,我的生产代码是

value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;
var value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;

生成的SQL是某物=@p@p=NULL。似乎EF正确地翻译了常量表达式,但如果涉及变量,它会将其视为正常比较。实际上是有道理的。

Linq到SQL的变通方法:

var result = from entry in table
             where entry.something.Equals(value)
             select entry;
Linq到实体的解决方案(哎哟!):

这是一只咬了我好几次的讨厌的虫子。如果此错误也影响了您,请访问并让Microsoft知道此错误也影响了您


编辑:!谢谢大家投票修正这个错误

为了向后兼容,它将是opt-in-您需要手动启用设置以使
entry==value
工作。还没有关于这个设置的消息。请继续收看


编辑2:根据EF团队的说法,此问题已在EF6中修复!呜呼

我们更改了EF6的默认行为以补偿三值逻辑


这意味着依赖于旧行为的现有代码(
null!=null,但仅在与变量比较时)需要更改为不依赖该行为,或者设置为false以使用旧的中断行为。

有一个稍微简单的解决方法,可用于LINQ to实体:

var result = from entry in table
         where entry.something == value || (value == null && entry.something == null)
         select entry;
正如AZ所注意到的,这是因为LINQ对实体的特殊情况x==null(即对null常量的相等比较),并将其转换为x为null

我们目前正在考虑更改此行为,以便在等式的两侧都可为空时自动引入补偿比较。不过,还有一些挑战:

  • 这可能会破坏已经依赖于现有行为的代码
  • 即使很少使用null参数,新的转换也可能影响现有查询的性能

  • 在任何情况下,我们是否着手这项工作将在很大程度上取决于我们的客户分配给它的相对优先级。如果您关心这个问题,我鼓励您在我们的新功能建议网站上投票支持它。

    我无法评论divega的帖子,但在这里介绍的不同解决方案中,divega的解决方案产生了最好的SQL。性能方面和长度方面。我刚刚使用SQLServerProfiler进行了检查,并查看了执行计划(使用“SET STATISTICS PROFILE ON”)

    个人使用,我更喜欢:

    var result = from entry in table    
                 where (entry.something??0)==(value??0)                    
                  select entry;
    
    结束


    因为它可以防止重复——虽然这在数学上并不精确,但它非常适合大多数情况。

    不幸的是,在EntityFramework5DBContext中,这个问题仍然没有得到解决

    我使用了这个变通方法(适用于MSSQL 2012,但在任何未来的MSSQL版本中,ANSI NULLS设置都可能被弃用)


    需要注意的是,这是一个肮脏的解决方法,但它可以非常快速地实现,并且适用于所有查询。

    由于Entity Framework 5.0,您可以使用以下代码来解决您的问题:

    public abstract class YourContext : DbContext
    {
      public YourContext()
      {
        (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;
      }
    }
    

    这将解决您的问题,因为实体框架将使用“C#like”空比较

    指出所有实体框架<6.0的建议都会生成一些笨拙的SQL。请参见第二个“清洁”修复示例

    荒谬的变通方法
    如果您喜欢像我一样使用方法(lambda)语法,您可以这样做:

    var result = new TableName();
    
    using(var db = new EFObjectContext)
    {
        var query = db.TableName;
    
        query = value1 == null 
            ? query.Where(tbl => tbl.entry1 == null) 
            : query.Where(tbl => tbl.entry1 == value1);
    
        query = value2 == null 
            ? query.Where(tbl => tbl.entry2 == null) 
            : query.Where(tbl => tbl.entry2 == value2);
    
        result = query
            .Select(tbl => tbl)
            .FirstOrDefault();
    
       // Inspect the value of the trace variable below to see the sql generated by EF
       var trace = ((ObjectQuery<REF_EQUIPMENT>) query).ToTraceString();
    
    }
    
    return result;
    
    var result=new TableName();
    使用(var db=新的EFObjectContext)
    {
    var query=db.TableName;
    query=value1==null
    ?query.Where(tbl=>tbl.entry1==null)
    :query.Where(tbl=>tbl.entry1==value1);
    query=value2==null
    ?query.Where(tbl=>tbl.entry2==null)
    :query.Where(tbl=>tbl.entry2==value2);
    结果=查询
    .选择(tbl=>tbl)
    .FirstOrDefault();
    //检查下面跟踪变量的值以查看EF生成的sql
    var trace=((ObjectQuery)query.ToTraceString();
    }
    返回结果;
    
    我认为这没有什么意义。。。连接器应该有点智能,不要求我们完成它的工作:在SQL中对正确的C#查询执行正确的转换。这会产生一个意外的行为。我支持Julien,这是EFS的一个失败这是标准的失败,现在与null的比较永久性地导致SQL Server 2016的未定义,ANSI NULLs永久性地设置为on,这只会变得更糟。Null可能表示未知值,但“Null”本身不是未知值。将空值与空值进行比较绝对会得到true,但不幸的是,该标准背离了常识和布尔逻辑。我投了赞成票
    public abstract class YourContext : DbContext
    {
      public YourContext()
      {
        (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;
      }
    }
    
    // comparing against this...
    Foo item = ...
    
    return DataModel.Foos.FirstOrDefault(o =>
        o.ProductID == item.ProductID
        // ridiculous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948
        && item.ProductStyleID.HasValue ? o.ProductStyleID == item.ProductStyleID : o.ProductStyleID == null
        && item.MountingID.HasValue ? o.MountingID == item.MountingID : o.MountingID == null
        && item.FrameID.HasValue ? o.FrameID == item.FrameID : o.FrameID == null
        && o.Width == w
        && o.Height == h
        );
    
    SELECT TOP (1) [Extent1].[ID]                 AS [ID],
           [Extent1].[Name]               AS [Name],
           [Extent1].[DisplayName]        AS [DisplayName],
           [Extent1].[ProductID]          AS [ProductID],
           [Extent1].[ProductStyleID]     AS [ProductStyleID],
           [Extent1].[MountingID]         AS [MountingID],
           [Extent1].[Width]              AS [Width],
           [Extent1].[Height]             AS [Height],
           [Extent1].[FrameID]            AS [FrameID],
    FROM   [dbo].[Foos] AS [Extent1]
    WHERE  (CASE
      WHEN (([Extent1].[ProductID] = 1 /* @p__linq__0 */)
            AND (NULL /* @p__linq__1 */ IS NOT NULL)) THEN
        CASE
          WHEN ([Extent1].[ProductStyleID] = NULL /* @p__linq__2 */) THEN cast(1 as bit)
          WHEN ([Extent1].[ProductStyleID] <> NULL /* @p__linq__2 */) THEN cast(0 as bit)
        END
      WHEN (([Extent1].[ProductStyleID] IS NULL)
            AND (2 /* @p__linq__3 */ IS NOT NULL)) THEN
        CASE
          WHEN ([Extent1].[MountingID] = 2 /* @p__linq__4 */) THEN cast(1 as bit)
          WHEN ([Extent1].[MountingID] <> 2 /* @p__linq__4 */) THEN cast(0 as bit)
        END
      WHEN (([Extent1].[MountingID] IS NULL)
            AND (NULL /* @p__linq__5 */ IS NOT NULL)) THEN
        CASE
          WHEN ([Extent1].[FrameID] = NULL /* @p__linq__6 */) THEN cast(1 as bit)
          WHEN ([Extent1].[FrameID] <> NULL /* @p__linq__6 */) THEN cast(0 as bit)
        END
      WHEN (([Extent1].[FrameID] IS NULL)
            AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
            AND ([Extent1].[Height] = 16 /* @p__linq__8 */)) THEN cast(1 as bit)
      WHEN (NOT (([Extent1].[FrameID] IS NULL)
                 AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
                 AND ([Extent1].[Height] = 16 /* @p__linq__8 */))) THEN cast(0 as bit)
    END) = 1
    
    // outrageous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948
    Expression<Func<Foo, bool>> filterProductStyle, filterMounting, filterFrame;
    if(item.ProductStyleID.HasValue) filterProductStyle = o => o.ProductStyleID == item.ProductStyleID;
    else filterProductStyle = o => o.ProductStyleID == null;
    
    if (item.MountingID.HasValue) filterMounting = o => o.MountingID == item.MountingID;
    else filterMounting = o => o.MountingID == null;
    
    if (item.FrameID.HasValue) filterFrame = o => o.FrameID == item.FrameID;
    else filterFrame = o => o.FrameID == null;
    
    return DataModel.Foos.Where(o =>
        o.ProductID == item.ProductID
        && o.Width == w
        && o.Height == h
        )
        // continue the outrageous workaround for proper sql
        .Where(filterProductStyle)
        .Where(filterMounting)
        .Where(filterFrame)
        .FirstOrDefault()
        ;
    
    SELECT TOP (1) [Extent1].[ID]                 AS [ID],
               [Extent1].[Name]               AS [Name],
               [Extent1].[DisplayName]        AS [DisplayName],
               [Extent1].[ProductID]          AS [ProductID],
               [Extent1].[ProductStyleID]     AS [ProductStyleID],
               [Extent1].[MountingID]         AS [MountingID],
               [Extent1].[Width]              AS [Width],
               [Extent1].[Height]             AS [Height],
               [Extent1].[FrameID]            AS [FrameID],
    FROM   [dbo].[Foos] AS [Extent1]
    WHERE  ([Extent1].[ProductID] = 1 /* @p__linq__0 */)
       AND ([Extent1].[Width] = 16 /* @p__linq__1 */)
       AND ([Extent1].[Height] = 20 /* @p__linq__2 */)
       AND ([Extent1].[ProductStyleID] IS NULL)
       AND ([Extent1].[MountingID] = 2 /* @p__linq__3 */)
       AND ([Extent1].[FrameID] IS NULL)
    
    var result = new TableName();
    
    using(var db = new EFObjectContext)
    {
        var query = db.TableName;
    
        query = value1 == null 
            ? query.Where(tbl => tbl.entry1 == null) 
            : query.Where(tbl => tbl.entry1 == value1);
    
        query = value2 == null 
            ? query.Where(tbl => tbl.entry2 == null) 
            : query.Where(tbl => tbl.entry2 == value2);
    
        result = query
            .Select(tbl => tbl)
            .FirstOrDefault();
    
       // Inspect the value of the trace variable below to see the sql generated by EF
       var trace = ((ObjectQuery<REF_EQUIPMENT>) query).ToTraceString();
    
    }
    
    return result;