【问题标题】:Boolean shortcircuit evaluation in Linq lambda expressionLinq lambda 表达式中的布尔短路评估
【发布时间】:2013-01-09 07:09:15
【问题描述】:

我有以下 Linq lambda 表达式:

private IEnumerable<SubjectSelectorSubjectGroup> GetSubjectList()
{
    User user = db.Users.Find(WebSecurity.CurrentUserId);
    return db.RequiredSubjects.Where(r => !r.Subject.Name.Contains("Home"))
                              .GroupBy(r => r.Subject)
                              .OrderByDescending(r => r.Count())
                              .Select(r => new SubjectSelectorSubjectGroup()
                              {
                                  SubjectId = r.Key.SubjectId,
                                  SubjectName = r.Key.Name,
                                  IsInFavourites = HttpContext.Current.Request.IsAuthenticated &&
                                                  (user.Elective1 != null && user.Elective1.SubjectId == r.Key.SubjectId ||
                                                   user.Elective2 != null && user.Elective2.SubjectId == r.Key.SubjectId ||
                                                   user.Elective3 != null && user.Elective3.SubjectId == r.Key.SubjectId),
                                  Occurrences = r.Count()
                              });
}

当用户未登录时,此函数中的user 变量为空。这应该不是问题,因为短路布尔评估应该处理这个问题。问题是,它没有!相反,System.NullReferenceException 被抛出。

当用户为 null 时 HttpContext.Current.Request.IsAuthenticated 返回 false。我通过注释掉引用 user 变量的括号部分来检查这一点,然后表达式正确计算。

有谁知道为什么Linq to Sql 试图在这种情况下取​​消对user 变量的引用,而实际上并不需要它?有没有人可以解决这个问题?

【问题讨论】:

  • NullReferenceException 当引用为空在内存中时被抛出。这表明该查询的一部分未转换为 SQL,而是在内存中进行评估。你能检查发出的 SQL 查询吗?罪魁祸首应该是没有翻译成SQL的部分。
  • 嗨格特。我已经通过创建 List 解决了这个问题。但作为一个有趣的问题,我如何检查发出的 SQL 查询?我是 Linq 的新手,所以仍然想弄清楚这些东西。感谢您的评论。
  • 你可以使用DataContext.Log,当然也可以使用Sql profiler。
  • 感谢您提供的信息,格特。

标签: c# linq linq-to-sql lambda


【解决方案1】:

整个表达式被转换为 SQL 并被评估为 SQL,这意味着 &amp;&amp; 运算符没有按预期短路。

您可以通过构建要搜索的ElectiveX.SubjectId 的列表或数组来解决问题,然后在查询中使用tmpList.Contains(r.Key.SubjectId)。这将被翻译成WHERE IN (...) SQL 表达式。

【讨论】:

  • 感谢 Anders 的解释和使用列表的建议。我按照您的建议使用列表解决了它。我会将代码作为我的解决方案的代码放在单独的答案中,但会将您的答案标记为正确。感谢您的帮助。
  • 我还观察到短路不能按预期工作。我不认为将 C# 表达式转换为 SQL 的事实,在幕后消除了 Microsoft 遵守 C# language specification 的义务,请参阅 7.12 条件逻辑运算符。
【解决方案2】:

这是我使用Anders Abel 提供的建议解决此问题的方法。

private IEnumerable<SubjectSelectorSubjectGroup> GetSubjectList()
{
    List<string> userSubjects = new List<string>();
    if (HttpContext.Current.Request.IsAuthenticated)
    {
        User user = db.Users.Find(WebSecurity.CurrentUserId);
        if (user.Elective1 != null) { userSubjects.Add(user.Elective1.SubjectId); }
        if (user.Elective2 != null) { userSubjects.Add(user.Elective2.SubjectId); }
        if (user.Elective3 != null) { userSubjects.Add(user.Elective3.SubjectId); }
    }

    return db.RequiredSubjects.Where(r => !r.Subject.Name.Contains("Home"))
                              .GroupBy(r => r.Subject)
                              .OrderByDescending(r => r.Count())
                              .Select(r => new SubjectSelectorSubjectGroup()
                              {
                                   SubjectId = r.Key.SubjectId,
                                   SubjectName = r.Key.Name,
                                   IsInFavourites = userSubjects.Contains(r.Key.SubjectId),
                                   Occurrences = r.Count()
                              });
}

【讨论】:

    【解决方案3】:

    我能想到这种行为的唯一原因是您的查询被翻译成 SQL (因为它的 LINQ to SQL),而对于 user.Elective 它是试图生成 CASE 语句。这就是您收到错误的原因。

    【讨论】:

    • 感谢 Habib 解释了为什么我的代码会抛出 NULL 异常。
    猜你喜欢
    • 2013-12-15
    • 1970-01-01
    • 2019-08-27
    • 2017-12-26
    • 1970-01-01
    • 2013-01-23
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    相关资源
    最近更新 更多