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

Yur*_*iuk 9 c# linq entity-framework asp.net-core ef-core-3.1

我已经为此苦苦挣扎了四天,但根本没有任何进展。有一个查询,在更新到 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()
                ))
Run Code Online (Sandbox Code Playgroud)

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

活动直到 ?? 12/31/9999 11:59:59 PM) .ThenByDescending(x => x.ServContrObjStructPlanVers.ActiveFrom) .ThenByDescending(x => x.ActiveFrom) .Select(x => x.PkServContrObjStructPlanEquipment()) .First无法翻译。以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。看 或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。看 或通过插入对 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());
Run Code Online (Sandbox Code Playgroud)

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

Include 中使用的 Lambda 表达式无效

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

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

Ste*_* Py 4

这看起来是 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();
Run Code Online (Sandbox Code Playgroud)

在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())一种浪费数据库时间的奇特方式。即 Children 包含最大的孩子。(当然是这样。)所以我只能假设我错过了一些东西,但我真的不想尝试在 6 个月后尝试获取这样的实体结构和查询..:)

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