LINQ to Entities和空字符串

15

我在一个使用EF 4.0作为数据库后端的ASP.NET 4.0 Web应用程序中遇到了一个相当奇怪的问题。基本上,我有一个存储用户密码重置请求的表(包含类型为byte []的重置密钥,类型为DateTime的过期时间以及指向包含string Emailstring NameUser的外键)。一些用户没有设置电子邮件地址,因此对于 PasswordRequest requestrequest.Emailnull

这里出现了问题。以下代码完全正常运行:

string u = Request["u"];
string e = Request["e"];

var requests = from r in context.PasswordRequests
               where r.User.Name == u && r.User.Email == null && r.Expiry >= DateTime.Now
               select r;

我得到了预期的结果数量(非零,因为存在包含null电子邮件的条目)。

但是当enull时,这总是返回一个空集合:

string u = Request["u"];
string e = Request["e"];

var requests = from r in context.PasswordRequests
               where r.User.Name == u && r.User.Email == e && r.Expiry >= DateTime.Now
               select r;

唯一能够正常工作的事情(在逻辑上并不合理)是:

string u = Request["u"];
string e = Request["e"];

IQueryable<PasswordRequest> requests;

if (e == null)
    requests = from r in context.PasswordRequests
               where r.User.Name == u && r.User.Email == null && r.Expiry >= DateTime.Now
               select r;
else
    requests = from r in context.PasswordRequests
               where r.User.Name == u && r.User.Email == e && r.Expiry >= DateTime.Now
               select r;

我完全被难住了,有什么想法吗?


你实际表中的电子邮件字段,是否声明为NULL列? - DoomerDGR8
你能发布生成的SQL吗? - Marius
4个回答

37

基本上这是SQL和C#在处理null时不匹配的问题。您不需要使用两个查询,但需要:

where r.User.Name == u && (r.User.Email == e ||
                           (e == null && r.User.Email == null))

这很烦人,可能有一个辅助函数可以让生活更轻松,但它根本来自于SQL的null处理,其中

where X = Y

如果X和Y都为null,则不匹配(而在C#中,等价的表达式会返回true)。

如果数据库中的u可为空值,则您可能也需要执行相同操作。

如果您希望将null和空字符串以相同方式处理,那么可以尝试一个小技巧:

// Before the query
e = e ?? "";

// In the query
where r.User.Name == u && (r.User.Email ?? "") == e

我相信这将对电子邮件列和e执行空值合并操作,因此您永远不会将空值与任何内容进行比较。


1
啊哈!我想可能会出现这种情况,但我不确定具体是什么原因导致的。感谢您的澄清! - hydroiodic
干得好!where条件有点宽松。所以我想把e的所有三个可能值都放进去是有意义的。 - DoomerDGR8
我遇到了一个问题,我的数据库包含一个允许空值的INT列,因此其他一些解决方案对我无效(大多数解决方案处理字符串)- 您最后的代码片段拯救了我的生命!谢谢!这是我最终使用的代码:(w.AccountID ?? _AccountID)== _AccountID - Losbear
这应该被视为Linq to Entities(或Linq to sql - 它也会在那里发生)中的一个错误。这在Connect上公开吗?我找不到任何信息。 - sinelaw
3
好的,我找到了它,并且在一些新版本的EF中似乎已经得到了修复:http://data.uservoice.com/forums/72025-ado-net-entity-framework-ef-feature-suggestions/suggestions/1015361-incorrect-handling-of-null-variables-in-where-cl?ref=title%23suggestion-1015361 - sinelaw

3

有用的链接。感谢您的发布! - hydroiodic

1
如果您像我一样更喜欢使用方法(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;

-1
如果您想在请求['e'] == null时从数据库中检索项目,
它应该是这样的:
var requests = from r in context.PasswordRequests
               where r.User.Name == u && r.User.Email is null && r.Expiry >= DateTime.Now
               select r;

请注意 == null 和 is null 是不同的。请参见 --> MSDN Info 因此,您最后的示例在某种程度上是有效的,因为您需要两种方法从 DB 获取数据,即如果电子邮件为空,则需要一种方法,而如果电子邮件 == Request['e'] ,则需要另一种方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接