Linq lambda表达式中的布尔短路评估

我有以下Linq lambda表达式:

private IEnumerable 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变量为null。 这不应该是一个问题,因为短路布尔评估应该处理这个问题。 问题是,它没有! 而是抛出System.NullReferenceException

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

有谁知道为什么Linq to Sql在这种情况下试图取消引用user变量时它实际上不应该是必要的? 有没有人可以解决这个问题?

整个表达式被转换为SQL并进行求值,这意味着&&运算符不会按预期短路。

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

以下是我使用Anders Abel给出的建议解决这个问题的方法。

 private IEnumerable GetSubjectList() { List userSubjects = new List(); 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() }); } 

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