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

时间:2020-03-04 12:02:17

标签: c# entity-framework linq 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()
                ))

现在它引发了一个异常:

LINQ表达式'DbSet。 => s3.RecStatus == 1).Where(s3 => EF.Property>((EntityShaperExpression: 实体类型:ServContrObjStructEquipment ValueBufferExpression: (ProjectionBindingExpression:Outer.Outer.Outer) IsNullable:False),“ PkServContrObjStructEquipment”)!= null && EF.Property>((EntityShaperExpression: 实体类型:ServContrObjStructEquipment ValueBufferExpression: (ProjectionBindingExpression:Outer.Outer.Outer) IsNullable:False),“ PkServContrObjStructEquipment”)== EF.Property>(s3,“ FkServContrObjStructEquipment”)) 选择(s3 => s3.PkServContrObjStructPlanEquipment) 包含(((MaterializeCollectionNavigation( 导航:导航:ServContrObjStructEquipment.ServContrObjStructPlanEquipment, 子查询:DbSet 哪里(s4 => s4.RecStatus == 1) 哪里(i => EF.Property>(((EntityShaperExpression: 实体类型:ServContrObjStructEquipment ValueBufferExpression: (ProjectionBindingExpression:Outer.Outer.Outer) IsNullable:错误 ),“ PkServContrObjStructEquipment”)!= null && EF.Property>((EntityShaperExpression: 实体类型:ServContrObjStructEquipment ValueBufferExpression: (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) 选择(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很熟悉。有没有一种方法可以重写此查询,以便可以将其翻译或做任何使其有效的查询? 谢谢!

1 个答案:

答案 0 :(得分:0)

这似乎是EF Core中的错误或某些重大更改(意外惊喜)。起初,我怀疑使用DateTime.MaxValue作为EF Core过去存在问题,但事实并非如此。由于某些原因,在“包含”内执行OrderBy子选择是例外。 EF Core 3从2.2.6的一个重大变化是,Core 2会自动切换到客户端表达式,其中Core会抛出异常,但是我能够通过探查器确认这样的查询已成功通过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()),几乎是一种浪费数据库时间的奇特方式。即孩子所在的地方,年龄最大的孩子。 (当然可以。)所以我只能假设我在那里错过了一些东西,但是我真的很讨厌尝试从现在开始的6个月内选择一个实体结构和查询。.:)

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