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

Posted

技术标签:

【中文标题】无法翻译 LINQ 表达式。以可翻译的形式重写查询,或切换到客户端评估 EF Core 3.1【英文标题】:The LINQ expression could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation EF Core 3.1 【发布时间】:2020-06-16 22:02:07 【问题描述】:

我已经为此苦苦挣扎了四天,但一点进展都没有。 有一个查询,在更新到 EF Core 3.1 之前工作得很好:

var equipments = await this.DbContext.ServContrObjStructEquipment
            .AsNoTracking()
            .Where(e => e.ServContrObjStruct.ServContrObjStructParent.ServContrObjStructParent.Objectuuid == sectionGuid)
            .Where(e => e.ServContrObjStructPlanEquipment.Select(x => x.PkServContrObjStructPlanEquipment).Contains(
                e.ServContrObjStructPlanEquipment.OrderByDescending(x => x.ServContrObjStructPlanVers.ActiveUntil ?? DateTime.MaxValue).ThenByDescending(x => x.ServContrObjStructPlanVers.ActiveFrom).ThenByDescending(x => x.ActiveFrom).Select(x => x.PkServContrObjStructPlanEquipment).FirstOrDefault()
                ))

现在它向我抛出一个异常,上面写着:

LINQ 表达式 'DbSet .Where(s3 => s3.RecStatus == 1) .Where(s3 => EF.Property>((EntityShaperExpression: 实体类型:ServContrObjStructEquipment 值缓冲区表达式: (ProjectionBindingExpression:Outer.Outer.Outer) IsNullable: False ), "PkServContrObjStructEquipment") != null && EF.Property>((EntityShaperExpression: 实体类型:ServContrObjStructEquipment 值缓冲区表达式: (ProjectionBindingExpression:Outer.Outer.Outer) IsNullable: False ), "PkServContrObjStructEquipment") == EF.Property>(s3, "FkServContrObjStructEquipment")) .Select(s3 => s3.PkServContrObjStructPlanEquipment) .Contains((MaterializeCollectionNavigation( 导航:导航:ServContrObjStructEquipment.ServContrObjStructPlanEquipment, 子查询:DbSet .Where(s4 => s4.RecStatus == 1) .Where(i => EF.Property>((EntityShaperExpression: 实体类型:ServContrObjStructEquipment 值缓冲区表达式: (ProjectionBindingExpression:Outer.Outer.Outer) IsNullable: 假 ), "PkServContrObjStructEquipment") != null && EF.Property>((EntityShaperExpression: 实体类型:ServContrObjStructEquipment 值缓冲区表达式: (ProjectionBindingExpression:Outer.Outer.Outer) IsNullable: 假 ), "PkServContrObjStructEquipment") == EF.Property>(i, "FkServContrObjStructEquipment"))) .AsQueryable() .OrderByDescending(x => x.ServContrObjStructPlanVers.ActiveUntil ?? 12/31/9999 11:59:59 PM) .ThenByDescending(x => x.ServContrObjStructPlanVers.ActiveFrom) .ThenByDescending(x => x.ActiveFrom) .Select(x => x.PkServContrObjStructPlanEquipment) .FirstOrDefault())' 无法翻译。要么以可翻译的形式重写查询,要么切换到客户评估 通过插入对 AsEnumerable() 的调用,显式地 AsAsyncEnumerable()、ToList() 或 ToListAsync()。看 https://go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。

我知道 EF Core 3.0-3.1 有重大更改,并且客户端评估现在在*** Select() 上执行。虽然我宁愿避免这样做,但我尝试调用所有ToList()AsEnumerable() 等来使其工作,但这也无济于事:查询复杂且多级,所以调用ToList()在任何时候,要么破坏它,要么没有得到相关记录(我猜是因为延迟加载),然后需要进一步执行查询。

我还尝试将查询拆分为单独的查询,看看那里发生了什么:

var intermEquipments = await this.DbContext.ServContrObjStructEquipment
                .AsNoTracking()
                .Include(e => e.ServContrObjStructPlanEquipment)
             .Where(e=>e.ServContrObjStruct.ServContrObjStructParent.ServContrObjStructParent.Objectuuid == sectionGuid)
                .ToListAsync();

            var intermEquipments1 = await this.DbContext.ServContrObjStructEquipment
                .AsNoTracking()
                .Include(e => e.ServContrObjStructPlanEquipment)
                .Where(e => e.ServContrObjStruct.ServContrObjStructParent.ServContrObjStructParent.Objectuuid == sectionGuid)
                .Select(e => e.ServContrObjStructPlanEquipment.Select(x => x.PkServContrObjStructPlanEquipment))
                .ToListAsync();

            var intermEquipments2 = this.DbContext.ServContrObjStructEquipment
                .AsNoTracking()
                .Include(e => e.ServContrObjStructPlanEquipment)
                .Where(e => e.ServContrObjStruct.ServContrObjStructParent.ServContrObjStructParent.Objectuuid == sectionGuid)
                .Select(e => e.ServContrObjStructPlanEquipment
                    .OrderByDescending(x => x.ServContrObjStructPlanVers.ActiveUntil ?? DateTime.MaxValue)
                                .ThenByDescending(x => x.ServContrObjStructPlanVers.ActiveFrom)
                                    .ThenByDescending(x => x.ActiveFrom)
                                        .Select(x => x.PkServContrObjStructPlanEquipment).ToList());

但它要么给我抛出一个异常,说

Include 中使用的 Lambda 表达式无效

或 NullReference(因为某些相关属性没有加载,正如我上面提到的)或根本不显示记录。

虽然我不是专家,但我对 EntityFramework 很熟悉。有没有办法重写这个查询,以便它可以被翻译或者只是做任何事情让它工作? 谢谢!

【问题讨论】:

为什么需要在拆分查询中包含。你有 this.NextreeContext.ServContrObjStructEquipment 和 .Include(e => e.ServContrObjStructPlanEquipment)。正因为您没有收到编译器错误,编译器正在识别对象 ServContrObjStructEquipment 并且您不需要包含。 看起来问题出在两个 Where 表达式之一。最有可能是第二个,但为了确定,先评论他们两个,然后取消评论其中一个,等等,直到你得到例外。 这对我来说似乎有点奇怪。将那些 where 语句变成连接不是更好吗? @IvanStoev 问题出在Contains 表达式上,请确认。 @Mike-314 你把它们变成连接是什么意思? 【参考方案1】:

这看起来是 EF Core 中的一个错误或某种重大变化(令人意外)。起初我怀疑过去使用 DateTime.MaxValue 作为 EF Core 存在问题,但事实并非如此。由于某种原因,在 Contains 内执行 OrderBy 子选择是例外。 EF Core 3 从 2.2.6 的一个重大变化是 Core 2 将自动切换到客户端表达式,其中 Core 将引发异常,但是我能够使用 Profiler 确认这样的查询已成功转换为使用 Core 的 SQL 2.2.6 但在 3.1 中它不翻译导致错误。此时,您可以向 EF Core 团队提交错误报告。

我能够通过更简单的查询重现这一点:

var results = context.Parents.Where(x => x.Children.Select(c => c.ChildId)
   .Contains(x.Children.OrderByDescending(c => c.BirthDate).Select(c => c.ChildId).FirstOrDefault()))
   .ToList();

在 2.2.6 中运行它成功编译为 SQL。 3.1 抛出异常。

准确理解此查询的返回含义可能会有所帮助,因为我已经阅读了 3 次,但仍然没有任何意义。 :)

您正在选择 ServerEquipment,其中他们的 Struct.Paremt.Parent.ObjectID = 部分 ID 并且他们的计划设备包含,据我所知,该集合中最早的设备记录。 Contains 检查是没有意义的,因为它只是在没有任何外部标准的情况下寻找该集合中是否存在选择行?我的意思是归结为它看起来像.Where(x => x.Children.Select(c => c.ChildId).Contains(x.Children.OrderByDescending(c2 => c2.BirthDate).Select(c2 => c2.ChildId).FirstOrDefault()),这几乎是一种浪费数据库时间的奇特方式。 IE。其中 Children 包含一个最大的孩子。 (当然可以。)所以我只能假设我在那里遗漏了一些东西,但我真的很不想在 6 个月后尝试获取一个实体结构和类似的查询.. :)

一个包含来自它自己的子集合的检查而没有任何外部标准是没有意义的。通常,这些类型的查询会查看子集合以查看它们是否包含某些匹配条件,这些条件将由覆盖的Where 子句中的Any() 类型检查来满足。再次查看您要过滤的确切内容以查看此查询是否可以简化并避免此重大更改可能会有所帮助。

【讨论】:

你好史蒂夫!感谢您为回答我的问题所做的努力!这个查询是在我之前写的,我也有支持它的麻烦。据我所知,有这样一个层次结构:结构->建筑->部分->区域->房间区域,所有这些实体都是数据库中的同一张表。所以这引入了自连接的需要,这可能发生在这个查询中。它必须为特定结构选择最新版本的结构平面图和放置在该平面图上的设备(此处为房间面积)。 哦,是的,当开发人员想出这样的数据结构时,一定会喜欢上它。 :) 尝试通过具有如此复杂结构的 *** 解决问题确实是一个难题。很明显,StructurePlans 是历史的,您想获得最新版本。装备呢?结构元素本身是历史的吗?例如,如果您找到特定部分的房间区域,是否有该房间区域的历史版本,或该房间区域中的历史设备集(例如每个计划)?

以上是关于无法翻译 LINQ 表达式。以可翻译的形式重写查询,或切换到客户端评估 EF Core 3.1的主要内容,如果未能解决你的问题,请参考以下文章

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

Linq 得到条件计数

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

Entityframework Core 3 linq表达式无法翻译

LINQ 表达式“无法翻译”

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