我有以下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
。
当用户为空时HttpContext.Current.Request.IsAuthenticated
返回false。我通过注释引用user
变量的括号部分来检查这一点,然后表达式正确评估。
有没有人知道为什么Linq to Sql
尝试在这种情况下取消引用user
变量时实际上不需要它?有没有人可以解决这个问题呢?
答案 0 :(得分:5)
整个表达式被转换为SQL并进行评估,这意味着&&
运算符不会按预期短路。
您可以通过构建要搜索的ElectiveX.SubjectId
的列表或数组来解决问题,然后在查询中使用tmpList.Contains(r.Key.SubjectId)
。这将被翻译成WHERE IN (...)
SQL表达式。
答案 1 :(得分:3)
以下是我使用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()
});
}
答案 2 :(得分:1)
我能想到这种行为的唯一原因是你的查询被翻译成SQL (因为它的LINQ to SQL),以及user.Elective
它试图生成一个CASE语句。这就是你得到错误的原因。