EF Core 2.0-筛选IQueryable导航属性集合为空

Lin*_*erg 3 asp.net-mvc entity-framework entity-framework-core

技术:

  • EF核心2.0.0
  • Asp.Net Core Mvc 2.0.0

当我执行此方法时,在评估CurrentGrade时会抛出“ InvalidOperationException:序列不包含任何匹配元素”。为什么会抛出?如何解决?

我有一个搜索方法,可对大型数据集(10000个用户和数千个相关实体)的很多属性进行过滤。我正在尝试优化查询,并且在完成所有筛选之前,我不希望执行查询。当使用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;
        }
Run Code Online (Sandbox Code Playgroud)

ApplyFiltering()可以很好地过滤外键,但是当使用.Where()过滤导航属性集合时,在将其包括在内之前,它会在Member上抛出ICollection Grades。

这是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;
    }
Run Code Online (Sandbox Code Playgroud)

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>();

}
Run Code Online (Sandbox Code Playgroud)

Iva*_*oev 5

问题在于,未映射(“计算”)属性正在引起客户端评估,但是在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));
Run Code Online (Sandbox Code Playgroud)