2011-02-10 63 views
11

我有一个名为“测试”表,其中只有1列中,“NullableInt”(可为空的int类型)LINQ如果使用可空int变量,准确的结果,如果使用“空”返回0结果

的记录是:1,2,空

int? nullableInt = null; 
var t = db.tests.Where(x => x.NullableInt == null).ToList(); // returns 1 record 
var t2 = db.tests.Where(x => x.NullableInt == nullableInt).ToList(); // returns 0 records 

出于某种原因,T2返回0的记录,即使寿它使用 “nullableInt” 变量,它具有null值,就像T,这是对 “空”

比较

任何帮助将不胜感激!

回答

2

查询可以构建这样:

var q = db.tests; 
if(nullableInt.HasValue) 
{ 
    q = q.Where(x => x.NullableInt == nullableInt.Value); 
} 
else 
{ 
    q = q.Where(x => x.NullableInt == null); 
} 
var t2 = q.ToList(); 
+0

+1这很糟糕,但它似乎是唯一的方法=( – Francisco 2011-12-14 15:45:24

+0

请参阅下面的答案,这是在EF6中修复的,您可以选择修复EF5中的文件 – 2013-06-12 18:15:42

6

是的 - 这是LINQ-to-SQL/Entity Framework中的一个错误。 IS NULL只有在您将空值硬编码到查询中时才会生成查询,而不是当前发生的变量为空。

第二个查询会产生

SELECT ....... 
WHERE NullableInt == @someParam 
WHERE @someParam is null. 

哪里,首先会产生WHERE子句中适当IS NULL

如果您使用的是LINQ to SQL,则可以将查询记录到Console.Out以查看您自己,如果您使用的是EF,那么ToTraceString()应该会显示相同的信息(或SQL Server事件探查器)

+2

其实我不认为这是一个错误,因为在第二个表达式中`nullableInt`不是`null`,即使你用`nullableInt = null`赋值它的值(因为它是一个结构体,它的值不能为空) 。因此,框架像其他结构一样对待它(如int)。 – 2011-02-10 05:20:25

+2

@Danny,这是分裂头发。该错误可能在规范中,即使代码完全符合规范说明它应该做的事情。换句话说,这里的错误可能不是代码碰巧做错了什么,但有人没有考虑这种情况。 – 2011-02-10 09:39:08

+1

确实分裂头发。没有正当理由解释为什么EF解析器应该生成第二个查询,而不是IS NULL。这已经被记录为MS的一个错误(投票数很高),尽管我手边没有这个链接。 – 2011-02-10 14:41:47

0

还有另一种解决方案,它会一直工作,尽管有一个小警告:

int? nullableInt = null; 
var t2 = db.tests.Where(x => object.Equals(x.NullableInt, nullableInt)).ToList(); 

当值为nullÿ OU将得到正确的IS NULL查询,但是当它不为空,你会得到这样的:

SELECT ... 
WHERE ([t0].[NullableInt] IS NOT NULL) AND ([t0].[NullableInt] = @p0) 

显然,它有额外的条件(其源是一种令人费解)。这就是说,SQL Server的查询优化器应该检测到这个,因为@ p0是一个非空值,第一个条件是一个超集,并将切断where子句。

0

会做:

var t2 = db.tests.Where(x => x.NullableInt == nullableInt ?? null).ToList(); 

工作?

虽然这似乎很疯狂。

2

TL;博士

如果您在使用EF6本的DbContext是固定的。

如果您使用EF5(或EF6中的ObjectContext),则需要将ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior设置为true。要在DbContext上执行此操作,请使用:

((IObjectContextAdapter)db).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true; 

更多细节

这个问题的根本原因是如何数据库进行比较空值以及如何C#比较空值的差异。因为你在C#中编写你的查询,你想使用C#的语义。

在EF5中,我们引入了ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior,它允许您选择使用C#语义而不是数据库语义。默认值为false(这样当您升级到EF5时,现有查询不会奇迹般地开始返回不同的结果)。但是您可以将其设置为true,并且您的查询都将返回行。

如果您在EF5使用的DbContext你需要下降到了ObjectContext的设置它:所以你是好去

((IObjectContextAdapter)db).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true; 

如果使用EF6,那么它已经被设置为true的DbContext 。我们认为这造成了如此多的混乱,值得对现有查询产生潜在的影响。