2017-08-25 155 views
3

技术:EF核心2.0 - 过滤的IQueryable导航属性集合空

  • EF核心2.0.0
  • Asp.Net核心的mvc 2.0.0

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

我有一个搜索方法过滤了大量数据集上的很多属性(拥有数千个相关实体的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; 
     } 

ApplyFiltering()可以很好地用于筛选外键,但使用。凡()上的导航属性集合过滤时在刚过滤之前被列入它洒在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是一个构件上的计算出的性能,等级仅仅是一个枚举:

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

尝试添加包含(X => x.CurrentGrade)到您的搜索查询的对象。 –

+0

@LazyCoder我收到一个异常: '处理请求时发生未处理的异常。 InvalidOperationException:属性'CurrentGrade'不是实体类型'Member'的导航属性。 'Include(string)'方法只能与'。'一起使用。导航属性名称的分隔列表。' – Lindeberg

+0

是的,我现在看到它,所以不是这样。但我认为,因为你正在做.AsNoTracking(),然后尝试在以后的时间(),它不喜欢...也许尝试删除它,不要.ToList()只是让它作为它是。 –

回答

4

的问题是,该未映射(“已计算”)属性正在导致client evaluation,但在EF评估Where子句的客户端部分时,导航属性尚未加载,因此您的Grades集合为空(因为它是i初始化为new Collection<MemberGrade> - 如果您删除初始化程序,那么您将得到NullReferenceException)。

现在,它可能被视为EF核心错误。但我强烈建议一般不会在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)); 
+0

非常感谢您的正确答案。你还可以提供一些关于如何使用LINQ to SQL中的计算属性来保持面向对象的想法吗? @Ivan Stoev我觉得必须有一个共同的最佳实践,但我很难在谷歌上找到它。有一些关于表达式的讨论,但仍然觉得不理想,因为逻辑应该是ON对象的属性或方法,对吗? – Lindeberg

+1

对。但是OOP,尤其是封装在LINQ查询翻译方面并不能很好地发挥作用,而LINQ查询翻译基于相反的原理 - 知识,即能够遍历表达树并映射可识别的方法/构造。那时根本没有任何东西。不久之后,在EFC中,您必须在(1)面向对象,但效率低下以及(2)非面向对象,但高效的情况之间进行选择。 –

+0

谢谢。神圣的狗屎,为什么不这样谈论更多我想知道,而与一个大型数据库工作..在EFC你说 - 你的意思是它在EF6是不同的?至少有支持我发现“DelegateDecompiler”的软件包,它可以解决这个问题。 – Lindeberg