EF Core 2.0 - 过滤IQueryable导航属性集合为空

时间:2017-08-25 21:22:31

标签: asp.net-mvc entity-framework entity-framework-core

技术:

  • EF Core 2.0.0
  • Asp.Net Core Mvc 2.0.0

当我执行此方法时,它会抛出" 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>();

}

1 个答案:

答案 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));
相关问题