无法翻译 LINQ 表达式“表达式”。以可以翻译的形式重写查询

Posted

技术标签:

【中文标题】无法翻译 LINQ 表达式“表达式”。以可以翻译的形式重写查询【英文标题】:The LINQ expression 'Expression' could not be translated. Either rewrite the query in a form that can be translated 【发布时间】:2021-10-13 04:30:33 【问题描述】:

我看过很多类似的问题,但没有一个能给我解决方案,所以我在想是否有人可以帮助我解决这个问题。我有一个实体层次结构,因为客户有多个 ClientRateDeals,然后我试图只获取那些具有所有通过某些条件的客户费率交易列表的客户。这是我的生成错误的 LINQ 查询:

            var query = _context.Client.Where(c=>c.Disabled==false)
                  .GroupJoin(_context.ClientRateDeal.Where(crd=>crd.Disabled==false),
                  c => c.Id,
                  crd => crd.ClientId,
                  (c, crd) => new
                  
                      c,
                      crd = crd.Where(cr => cr.DateEnd == null || cr.DateEnd > DateTime.Today)
                  )
                  .Where(res =>  res.crd.Count() == 0)
                  .Select(cl => cl.c).AsNoTracking().ToList();

正如您在结果选择器参数中看到的那样,我保留了该条件,然后在结果选择器上保留了 where 子句,以仅获取那些其客户费率交易计数为 0 的人。但是由于某种原因,我得到了以下异常LINQ 无法翻译。谁能帮我解决这个问题?

【问题讨论】:

使用导航属性Client.ClientRateDeals,而不是GroupJoin。如果您使用的是 EF 核心 5,则过滤 Include @GertArnold 我正在使用 EFCore 5,但为什么会出现该错误?我可以不使用 GroupJoin 来获得所需的结果吗? EF 核心对分组(GroupBy 和 GroupJoin)的支持非常有限。 @GertArnold 但是如果我不使用 groupjoin 或任何其他加入,我该如何实现这个条件,如果该客户端中的所有 clientratedeals 都通过了某些条件,我只需要选择一个客户端?你能帮我吗? @Gert 所说的是您应该拥有并使用 collection navigation property 而不是手动连接。没有这样的理由吗? 【参考方案1】:

由于未知原因(它与 GroupBy 没有任何相似之处),EF Core 3.x、5.x 不支持 LINQ GroupJoin 运算符。

您必须使用可用的替代方法之一 - (1) 集合导航属性(首选)或 (2) 相关子查询。

例如

(1) 在Client 类中定义

public ICollection<ClientRateDeal> ClientRateDeals  get; set; 

并在查询中使用它

var query = _context.Client
    .Where(c => c.Disabled==false)
    // vvv
    .Where(c => !c.ClientRateDeals.Any(
        crd => crd.Disabled == false &&
        (crd.DateEnd == null || crd.DateEnd > DateTime.Today)))
    .AsNoTracking().ToList();

或 (2)

var query = _context.Client
    .Where(c => c.Disabled==false)
    // vvv
    .Where(c => !_context.ClientRateDeal.Any(crd =>
        c.Id == crd.ClientId &&
        crd.Disabled == false &&
        cr.DateEnd == null || cr.DateEnd > DateTime.Today))
    .AsNoTracking().ToList();

一般来说,而不是

db.As.GroupJoin(db.Bs, a => a.Id, b => b.AId, (a, Bs) => new  a, Bs )

使用

db.As.Select(a => new  a, Bs = db.Bs.Where(b => a.Id == b.AId) )

相关的 github 问题(请投票以便有机会实现该问题):

Query with GroupBy or GroupJoin throws exception #17068

Query: Support GroupJoin when it is final query operator #19930

尽管第二个并不是我们所需要的(我们只想将GroupJoin 翻译成上面显示的相关子查询语法)。

【讨论】:

感谢您的帮助,但您确定 EF Core 5.x 不支持 GroupJoin,因为之前编写的查询代替了我所说的,并且工作正常如果您愿意,我可以给您那个查询。 @ShubhamTiwari 是的,我 100% 确定它不受支持,因为我与 EF Core 团队长期争夺它(请参阅链接),最后我当然输了 - 他们说 GroupJoin GroupBy 等价 由于模式匹配 GroupJoin 运算符的高成本,我们不会在 GroupJoin 中添加任何翻译,即使等价的 GroupBy 查询会被翻译。客户需要使用GroupBy 语法,因为这是最接近 SQL 的表示形式。 您提供的查询正在工作,但正如我在问题中所说的那样,如果任何内部实体(即我的客户内部的客户费率交易)没有通过我不想要的条件那个客户,但我刚刚测试过,其中 1 个客户有 2 个 CRD,1 个通过了条件,而其他没有,但我仍然得到与通过条件的 CRD 行相对应的那个客户!我该如何纠正? !Any(...) 应该完成这项工作,因为它过滤的是外部实体 (Client),而不是内部实体。 !Any()Count() == 0 相同,只是一般性能更高)。您是否还有其他未在此处显示的条件? 不,我只想获取所有客户费率交易 DateEnd 都在过去或为空的客户。如果任何客户费率交易在 DateEnd 中具有将来的值,我不想要该客户,也不应该禁用该客户。 var query = _context.Client .Where(c => c.Disabled == false) .Where(c => !c.ClientRateDeal.Any( crd => crd.Disabled == false && (crd.DateEnd == null | | crd.DateEnd > DateTime.Today))) .AsNoTracking().ToList();

以上是关于无法翻译 LINQ 表达式“表达式”。以可以翻译的形式重写查询的主要内容,如果未能解决你的问题,请参考以下文章

无法翻译 LINQ 表达式,将在本地计算

EF.Property 抛出“无法翻译 LINQ 表达式”

无法翻译 LINQ 表达式。以可翻译的形式重写查询,或切换到客户端评估 EF Core 3.1

无法翻译 LINQ 表达式 DbSet<>.Any

LINQ 表达式 - 使用 .Any in s Select 无法翻译

LINQ - '无法翻译表达式'与以前使用和验证的查询条件