【问题标题】:EF Core 2.0 - filtering IQueryable navigation property collection emptyEF Core 2.0 - 过滤 IQueryable 导航属性集合为空
【发布时间】:2018-02-03 23:40:45
【问题描述】:

技术:

  • EF Core 2.0.0
  • Asp.Net Core Mvc 2.0.0

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

我有一种搜索方法可以过滤大型数据集(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 Grades on Member。

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

}

【问题讨论】:

  • 尝试将 Include(x => x.CurrentGrade) 添加到您的搜索查询对象。
  • @LazyCoder 我得到一个例外:An unhandled exception occurred while processing the request. InvalidOperationException: The property 'CurrentGrade' is not a navigation property of entity type 'Member'. The 'Include(string)' method can only be used with a '.' separated list of navigation property names.
  • 是的,我现在看到了,所以不是这样。但我认为因为你正在做 .AsNoTracking() 然后在以后的生活中尝试 .Where() 它,所以它不喜欢这样......也许尝试放弃它,不要 .ToList() 就让它保持原样是。
  • @Lindeberg 在迁移到 EF Core 2.0 之前,我没有这样做。你确定你有 CurrentGrade 作为过滤器并且没有发生异常吗?
  • @LazyCoder 不使用 AsNoTracking() 只使用 AsQueryable() 没有区别。

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


【解决方案1】:

问题是未映射(“计算”)属性导致client evaluation,但在EF评估Where子句的客户端部分时,导航属性尚未加载,因此您的Grades集合为空(因为它已使用 new Collection&lt;MemberGrade&gt; 初始化 - 如果您删除初始化程序,那么您将获得 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));

【讨论】:

  • 感谢您提供的正确答案。您能否提供一些想法来保持它面向对象并在 LINQ to SQL 中使用这样的计算属性? @Ivan Stoev我觉得必须有一个共同的最佳实践,但我在谷歌上找不到它。有一些关于表达式的讨论,但仍然感觉不是最佳的,因为逻辑应该在具有属性或方法的对象上对吧?
  • 对。但是 OOP,尤其是封装不能很好地与基于相反原理的 LINQ 查询翻译 - 知识,即能够遍历表达式树并映射识别的方法/结构。当时根本没有任何物体。简而言之,在 EFC 中,您必须在 (1) 面向对象但效率低下和 (2) 非面向对象但效率高之间做出选择。
  • 谢谢。天哪,我想知道为什么在使用大型数据库时没有更多地谈论这个问题。在 EFC 中你说 - 你的意思是它在 EF6 中有所不同吗?至少有对我找到的“DelegateDecompiler”包的支持,它可以解决它。
  • 在 EF6 中,您甚至不能在查询中包含不可识别的方法。它编译成功(与任何有效的 C# 代码一样),但在运行时您会得到著名的 NotSupportedException LINQ to Entities 无法识别方法 'SomeMethodName' 方法,并且此方法无法转换为存储表达式。 i> DelegateDecompliler 是一个有趣的项目,我从未尝试过,但它看起来很有希望(只要它工作)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 2018-10-22
  • 2018-10-10
  • 2021-08-13
  • 2019-08-06
相关资源
最近更新 更多