技术:
当我执行此方法时,它会抛出" InvalidOperationException:在评估CurrentGrade时,Sequence不包含匹配的元素" 。它为什么抛出,我该如何解决?
我有一个搜索方法可以过滤大型数据集上的大量属性(拥有数千个相关实体的10 000个用户)。我试图优化查询,我不想执行查询,直到完成所有过滤。使用ToList()使方法工作时,我宁愿对IQueryable起作用,并在完成过滤时执行查询。
在将EF Core从1.x更新到2.0之前,我确信这有效。
public MemberQueryResult Search(MemberQuery filter)
{
var query = Context.Users
.Include(x => x.Honours)
.Include(x => x.Grades)
.Include(x => x.Strokes)
.Include(x => x.Posts)
.Include(x => x.Loge)
.AsNoTracking();
query = query.ApplyFiltering(filter);
return result;
}
ApplyFiltering()适用于对外键进行过滤,但是当使用.Where()过滤导航属性集时,它会在包含过滤之前在成员上的ICollection等级上抛出。
这是抛出的ApplyFiltering()内部的方法:
private static IQueryable<Member> SearchByCurrentGradeRange(MemberQuery filter, IQueryable<Member> result)
{
if (filter.GradeRange == null) return result;
var gradeRange = filter.GradeRange.Split(',');
var gradeFrom = (Grade)int.Parse(gradeRange[0]);
var gradeTo = (Grade)int.Parse(gradeRange[1]);
result = result.Where(x => x.CurrentGrade >= gradeFrom && x.CurrentGrade <= gradeTo);
return result;
}
CurrentGrade是成员的计算属性,Grade只是一个枚举。:
public sealed class Member : IdentityUser
{
public Grade CurrentGrade => Grades.OrderBy(x => x.Grade).Last(x => x.ReceivedDate != null).Grade;
public ICollection<MemberGrade> Grades { get; set; } = new Collection<MemberGrade>();
}
答案 0 :(得分:4)
问题是未映射(“计算”)属性导致client evaluation,但在EF评估Where
子句的客户端部分时,导航属性尚未加载,因此您的Grades
集合为空(因为它已使用new Collection<MemberGrade>
进行了初始化 - 如果您删除了初始化程序,那么您将获得NullReferenceException
)。
现在,它可能被视为EF Core错误。但我强烈建议一般在LINQ查询中使用不使用未映射的属性,尤其是在查询过滤条件中。即使它们有效,客户端评估也会导致在内存中加载大量数据,只是为了在那里应用过滤器,而不是在数据库(SQL)端。
还要确保使用SQL可翻译结构。例如,Last
/ LastOrDefault
没有自然的SQL翻译,而FirstOrDefault
则有,所以通常的模式是OrderByDescending().FirstOrDefault()
而不是OrderBy().LastOrDefault()
。
话虽如此,在您的情况下,工作服务器端评估解决方案将是这样的:
result = result.Where(m => m.Grades
.Where(x => x.ReceivedDate != null).OrderByDescending(x => x.Grade).Take(1)
.Any(x => x.Grade >= gradeFrom && x.Grade <= gradeTo));