在 Linq 中使用包含 Intersect/Union/Exclude

Using Include with Intersect/Union/Exclude in Linq

本来应该是一个相对简单的任务,却变成了一个令人惊讶的复杂问题。以至于我开始认为我的方法可能只是超出了 Linq 的功能范围。

我想做的是拼凑一个 Linq 查询,然后调用 .Include() 以从多个子实体中提取值。例如,假设我有这些实体:

public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Location { get; set; }
    public ISet<Child> Children { get; set; }
}

public class Child
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public Parent Parent { get; set; }
    public string Name { get; set; }
}

假设我想执行查询以从 Parent 检索记录,其中 Name 是某个值,Location 是其他值,然后包括 Child 也有记录。但无论出于何种原因,我不知道 NameLocation 同时的查询值,所以我必须采用两个单独的可查询对象并加入它们,例如:

MyDbContext C = new MyDbContext();
var queryOne = C.Parent.Where(p => p.Name == myName);
var queryTwo = C.Parent.Where(p => p.Location == myLocation);
var finalQuery = queryOne.Intersect(queryTwo);

效果很好,产生的结果与我刚刚完成的完全一样:

var query = C.Parent.Where(p => p.Name == myName && p.Location = myLocation);

同样,我可以:

var finalQuery = queryOne.Union(queryTwo);

给我结果就像我有:

var query = C.Parent.Where(p => p.Name == myName || p.Location = myLocation);

但是,一旦应用 Intersect()Union(),我无法做的就是使用 Include() 映射 Child,如:

finalQuery.Include(p => p.Children);

此代码将编译,但产生如下结果:

  1. Union() 的情况下,将生成结果集,但不会枚举 Child 个实体。
  2. Intersect() 的情况下,尝试应用 Include() 时会生成 运行 时间错误,如下所示:

Expression of type 'System.Collections.Generic.IEnumerable`1[Microsoft.EntityFrameworkCore.Query.Internal.AnonymousObject]' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable`1[System.Object]' of method 'System.Collections.Generic.IEnumerable`1[System.Object] Intersect[Object](System.Collections.Generic.IEnumerable`1[System.Object], System.Collections.Generic.IEnumerable`1[System.Object])'

令我困惑的是这段代码将完全按预期工作:

var query = C.Parent.Where(p => p.Name == myName).Where(p => p.Location == myLocation);
query.Include(p => p.Children);

即,根据需要的结果,包括 Child 个枚举的实体。

my methodology perhaps is simply out of scope with the capabilities of Linq

问题不是 LINQ,而是 EF Core 查询翻译,特别是缺少 Intersect / Union / Concat / Except 方法 SQL 翻译,由 #6812 Query: Translate IQueryable.Concat/Union/Intersect/Except/etc. to server 跟踪。

很快,此类查询目前在您的案例 #1 中使用 client evaluation, which with combination of how the EF Core handles Include leads to many unexpected runtime exceptions (like your case #2) or wrong behaviors (like Ignored Includes

因此,根据 EF Core 团队负责人的回应,虽然您的方法在技术上完全合理

Changing this to producing a single SQL query on the server isn't currently a top priority

所以目前甚至没有计划在 3.0 版本中发布,尽管有计划更改(重写)整个查询翻译管道,这也可能允许实现它。

目前,您别无选择。您可以尝试自己处理查询表达式树,但这是一项复杂的任务,您可能会发现它尚未实现的原因:) 如果您可以将查询转换为具有组合 Where 条件的等效单个查询,那么申请 Include 就可以了。


P.S。请注意,即使现​​在您的方法在技术上 "works" w/o Include,在客户端评估它的方式方面,它绝对不等同于相应的单个查询。

很长一段时间过去了,但是这个 .Include 问题在 EF 6 中仍然存在。但是,有一个解决方法:在 intersecting/Unionizing 之前附加每个子请求 .Include

MyDbContext C = new MyDbContext();
var queryOne = db.Parents.Where(p => p.Name == parent.Name).Include("Children");
var queryTwo = db.Parents.Where(p => p.Location == parent.Location).Include("Children");
var finalQuery = queryOne.Intersect(queryTwo);

正如@Ivan Stoev 所述,Intersection/Union 是用后取数据完成的,而 .Include 在请求时是可以的。

所以,截至目前,您可以使用这一选项。