C# 实体框架核心3.0对包含集合导航属性的性能影响(笛卡尔爆炸)

C# 实体框架核心3.0对包含集合导航属性的性能影响(笛卡尔爆炸),c#,performance,entity-framework,.net-core,C#,Performance,Entity Framework,.net Core,在将EF Core 2.2升级到EF Core 3.0之后,我们面临一个重大的性能问题。想象一个简单的数据模型,它有一个集合导航属性和数百个字段(现实看起来更黑暗): 在项目检索过程中,我们查询如下: ... var myQueryable = this._context.Items.Include(i => i.AddInfos).Where(**some filter**); ... // moar filters var result = myQueryable.ToList();

在将EF Core 2.2升级到EF Core 3.0之后,我们面临一个重大的性能问题。想象一个简单的数据模型,它有一个集合导航属性和数百个字段(现实看起来更黑暗):

在项目检索过程中,我们查询如下:

...
var myQueryable = this._context.Items.Include(i => i.AddInfos).Where(**some filter**);
... // moar filters
var result = myQueryable.ToList();
一直向前,直到这一点

在EF 2.2中,获取该可查询项会导致两个单独的查询,一个用于
,另一个用于
附加信息
-级。这些查询通常获取10.000个
项目
和大约250.000个
附加信息

然而,在EF Core 3.0中,只生成一个查询,将
AddInfo
加入到
Item
中,乍一看似乎是更好的选择。但是,我们的
需要使用所有100多个字段来获取,这就是为什么投影到较小的类或匿名类型(添加对.Select(…)-方法的调用)是不可行的。因此,结果集中有太多冗余(每个
项大约有25次),以至于查询本身需要太长时间才能在可接受的时间内运行

EF Core 3.0是否提供了任何选项,使我们能够在不大量更改数据模型的情况下,2.2次切换回良好的旧EF Core的查询行为?我们已经从应用程序的其他部分的这一更改中获益,但在这个特定场景中没有

非常感谢

更新


经过进一步调查,我发现微软已经解决了这个问题,而且开箱即用,似乎无法配置拆分查询执行。

根据我最初问题的更新,见解甚至让我自己确信,实际上,没有内置配置可返回到拆分查询执行

但是,MS提供了一些代码示例,说明如何在代码更改最少的情况下实现这一点(对于我们的用例!)

我们只是删除对集合导航属性的.Include(…)调用(在本例中,1:n关系不受影响!)。获取项目后,我们只需使用以下命令进行另一次调用:

...
var myQueryable = this._context.Items.Where(**some filter**);
... // moar filters
var result = myQueryable.ToList();
...
var addInfos = myQueryable.Include(x => x.AddInfos).SelectMany(x => x.AddInfos).Select(x => new {x.ItemID, x}).ToList();

这将获取集合导航属性实体,如果启用了更改跟踪,则会自动填充
结果
变量中各个项目的集合。

是通过web连接(http)进行的查询吗?Core2.2和Core3.0中的默认头可能不同(如http版本1.0和http版本1.1)。您可能需要使用诸如wireshark或fiddler之类的嗅探器,并检查第一个请求以验证标头是否相同。请阅读以下内容。你是对的,行为改变了,它与旧的EF 6.x行为相匹配。我不明白的是,EF2.2是如何将其转换为2个查询的。你有这方面的样本吗?@Eldar,谢谢你提供链接。转换为两个查询是可能的,因为使用了相同的筛选器(->WHERE子句)从Item和AddInfo表中获取数据,而后者当然带有到Item表本身的连接。EF将项目及其集合导航属性重新缝合到内存中。您是否也可以共享第二个查询的输出(生成的sql查询)。
...
var myQueryable = this._context.Items.Include(i => i.AddInfos).Where(**some filter**);
... // moar filters
var result = myQueryable.ToList();
...
var myQueryable = this._context.Items.Where(**some filter**);
... // moar filters
var result = myQueryable.ToList();
...
var addInfos = myQueryable.Include(x => x.AddInfos).SelectMany(x => x.AddInfos).Select(x => new {x.ItemID, x}).ToList();