【问题标题】:Using Include with Intersect/Union/Exclude in Linq在 Linq 中使用 Include 与 Intersect/Union/Exclude
【发布时间】:2019-04-13 12:59:13
【问题描述】:

原本应该是一项相对简单的任务,现在却变成了一个令人惊讶的复杂问题。以至于我开始认为我的方法可能超出了 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() 时会生成运行时错误,如下所示:

类型的表达 'System.Collections.Generic.IEnumerable`1[Microsoft.EntityFrameworkCore.Query.Internal.AnonymousObject]' 不能用于类型参数 'System.Collections.Generic.IEnumerable`1[System.Object]' 的方法 'System.Collections.Generic.IEnumerable`1[System.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 实体。

【问题讨论】:

  • 那你为什么不直接使用var query = C.Parent.Where(p =&gt; p.Name == myName); query = query.Where(p =&gt; p.Location == myLocation); query = query.Include(p =&gt; p.Children);呢?有没有你没有的东西?
  • 嗯,这将适用于加入查询的 OR(联合)条件,但不适用于 AND(相交)。
  • 恰恰相反。对Where 的两次调用对最终查询执行AND
  • 对,这就是我的意思...适用于 AND 条件,但不适用于 OR。我两个都需要。
  • 在某些时候你有两种价值观,不是吗?等到你这样做了。

标签: c# linq entity-framework-core entity-framework-core-2.1


【解决方案1】:

我的方法可能完全超出了 Linq 的能力范围

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

简而言之,此类查询当前使用 client evaluation,结合 EF Core 如何处理 Include 会导致许多意外的运行时异常(如您的案例 #2)或错误行为(如您的案例中的 Ignored Includes # 1).

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

将其更改为在服务器上生成单个 SQL 查询目前不是首要任务

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

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


附:请注意,即使现​​在您的方法在技术上“有效”,没有 Include,但在客户端评估它的方式上,它绝对不等同于相应的单个查询。

【讨论】:

  • 非常感谢。我四处寻找诸如此类的已知问题,但找不到任何问题。看起来我需要显着改变我的方法。我将不得不生成一些相当复杂的代码来附加 Where() 条件等。或者,也许有一些方法可以使用委托来创建我需要的复杂条件表达式。无论如何,很高兴知道这是一个内部问题,我现在可以停止尝试让它发挥作用。谢谢! :-)
  • 在再次查看您的答案时,我想补充一点,我的目标确实是在服务器上执行单个查询。我的基线总是有点盲目地接受这总是最好的方法。如果我的逻辑要求我从服务器获取一些结果,然后必须对一组或多组结果做某事以获得我最终想要的东西,我总是不喜欢它。我觉得 ORM 应该正确地为我处理所有这些。我也不喜欢制定大而笨重的 Linq 查询,但看起来这就是我要做的......
  • @MichaelDoleman 绝对。我同意 ORM 应该为你处理这个问题。恕我直言,这是当前 EF Core 阶段的主要失败之一,相比之下,旧的好 EF6 会为您完美地处理它,正如预期的那样。 EF Core 目前只用于简单的 CRUD,仅此而已。
【解决方案2】:

很长一段时间过去了,但这个.Include 问题在EF 6 中仍然存在。但是,有一个解决方法:在相交/联合之前附加.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 在请求时是可以的。

所以,到目前为止,您可以使用这个选项。

【讨论】:

  • 是的,但问题是为什么Include 不能应用于最终查询。你不回答这个问题。这只是一种变通方法。
  • OP 询问“将 Linq 查询拼凑在一起,然后调用 .Include() 以从多个子实体中提取值”。我回答了一个解决方法:“用其子实体填充以提取值的拼凑 Linq 查询”。我提到这是一种解决方法:这是可用于解决 Include 无法应用于最终查询的事实的一些选项之一。确实,这是一种解决方法。恕我直言,仍然比you have no options 更好的答案。
  • 确切地说,“拼凑一个 Linq 查询并然后调用.Include()”。
  • 我遇到了与 OP 完全相同的问题,因此,我突然想到了这个解决方法。认为“有一种解决方法”会比“没有选择”更友好。我的回答有什么问题吗? - 请注意,在 .Union/.Intersect 之前填充两个 .Where 会降低性能;但这是另一个问题(我相应地修改了自己的查询,我没有人判断 OP 的示例用例)。
猜你喜欢
  • 2018-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-13
  • 2023-02-11
  • 2021-05-10
  • 2012-05-08
  • 2011-04-25
相关资源
最近更新 更多