我有一个名为“test”的表,它只有1列,“NullableInt”(可为空的int类型)
记录是:1,2,null
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,它与“null”进行比较
任何帮助将不胜感激!
查询可以这种方式构建:
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();
查询可以这种方式构建:
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();
是的 - 这是LINQ-to-SQL / Entity Framework中的一个错误。 IS NULL
如果您将null硬编码到查询中,而不是当前为空的变量,则只会生成查询。
第二个查询将生成
SELECT .......
WHERE NullableInt == @someParam
WHERE @someParam is null.
第一个会产生适当的 IS NULL
在里面 WHERE
条款。
如果你正在使用LINQ-to-SQL,你可以将你的查询记录到Console.Out以便自己查看,如果你正在使用EF,那么ToTraceString()应该显示相同的信息(或SQL Server探查器)
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,那么它已经在DbContext上设置为true,所以你很高兴。我们认为这导致了如此多的混乱,值得对现有查询产生潜在影响。
还有另一个解决方案永远有效,尽管有一个小小的警告:
int? nullableInt = null;
var t2 = db.tests.Where(x => object.Equals(x.NullableInt, nullableInt)).ToList();
当值为null时,您将获得正确的值 IS NULL
查询,但是当它不为null时,你会得到类似的东西:
SELECT ...
WHERE ([t0].[NullableInt] IS NOT NULL) AND ([t0].[NullableInt] = @p0)
显然它有一个额外的条件(其来源有点令人费解)。话虽这么说,SQL Server的查询优化器应检测到,因为@ p0是一个非空值,第一个条件是超集,并将删除where子句。
会做:
var t2 = db.tests.Where(x => x.NullableInt == nullableInt ?? null).ToList();
工作?
但这似乎完全是疯狂的。