[已更新 - 请参阅底部的更新]
我使用 EF 代码优先,并且总体上对此感到满意。然而,一个简单(且常见)的操作导致 EF 生成极其复杂的 SQL,这降低了我的应用程序的速度。
我只是使用(整数)ID 列表来获取一组实体,但因为我需要大量子实体的详细信息,所以我使用.Include()
让这些子实体同时加载,如下:
db.MyEntities
.Where(x => x.ClientId == clientId)
.Where(x => ids.Contains(x.Id))
.Where(x => x.SubEntity1 != null)
.Include(x => x.SubEntity1)
.Include(x => x.SubEntity1.SubSubEntity1)
.Include(x => x.SubEntity1.SubSubEntity2)
.Include(x => x.SubEntity1.SubSubEntity3)
.Include(x => x.SubEntity1.SubSubEntity4)
.Include(x => x.SubEntity2)
.Include(x => x.SubEntity2.SubSubEntity1)
.Include(x => x.SubEntity2.SubSubEntity2)
.Include(x => x.SubEntity2.SubSubEntity3)
.Include(x => x.SubEntity2.SubSubEntity4)
.Include(x => x.SubEntity3)
正如您所看到的,除了所有这些之外,这并不是一个特别复杂的查询Include
s.
EF 为此生成的 SQL 是huge- 大约74KbSQL 的。执行时间不会很长(因为通常 ID 列表中的项目数量很少),但需要 EF构建查询需要花费超过一秒的时间- 即在查询被发送到数据库之前。
如果我删除Includes
,那么查询就会小得多,整个过程花费的时间也少得多 - 但每次都会加载各种相关实体,这不能很好地扩展。
EF 似乎为我提供了两种加载数据的选项:
- 在初始查询期间一次加载所有子实体(使用
Include
如上所述),或
- 一次加载一个子实体(使用延迟加载,或显式使用
Load
/LoadProperty
).
如果可行的话,选项 1 将是我的首选选项,但由于在这种情况下不起作用,我唯一剩下的选项是 2 - 我认为这是不可接受的:会有太多数据库查询,其中输入列表为ID(即实体的数量)很大。
在我看来,EF 似乎没有解决另一个选项:获取主要实体后,获取所有相关的 SubEntity1 实体,然后获取所有相关的 SubEntity2 实体,等等。这样,查询的数量与的数量types要获取的实体的数量,而不是实体的数量。这会扩展得更好。
我看不到在 EF 中执行此操作的方法:换句话说,就是说“为所有这些实体加载此属性(在单个查询中)”。
我是否必须放弃 EF 并编写自己的 SQL?
UPDATE我注意到即使我删除了Include
s,生成的 SQL 比我想象的更复杂,我认为这一切都源于 EF 不“喜欢”我的表结构。我花了好几天的时间才让 EF 通过 Code First(和 Fluent API)创建我正在寻找的数据库结构,即使我已经(几乎)达到了我想要的目标,我也不得不接受一些妥协。
我想我现在要为敢于做 EF 不希望我做的事情而付出进一步的惩罚。看起来一个简单的查询比应有的更复杂,而稍微复杂的查询则复杂得多。
这是令人难以置信的沮丧 - 我以为我已经把所有这些 EF 麻烦抛在脑后,而该系统现在已经投入生产,有数十个用户 - 这将使我很难重新开始。
看来我得花上一辈子的时间与 EF 拼尽全力地战斗。我多么希望我一开始就没有使用过它!
不管怎样,回到我原来的问题:如果我有一堆 A 类型的实体,我想为其加载 B 类型的相关子实体在一个查询中, 有没有办法做到这一点?