3

[更新-见底部更新]

我使用的是 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)

如您所见,这不是一个特别复杂的查询,除了所有这些Includes.

EF 为此生成的 SQL 是巨大的- 大约74Kb的 SQL。执行并不需要很长时间(因为通常 ID 列表中的项目数量很少),但是仅构建查询就需要 EF 超过一秒- 即在查询甚至发送到数据库之前。

如果我删除Includes,则查询要小得多,整个事情花费的时间要少得多 - 但是各种相关实体然后一次加载一个,这不能很好地扩展。

EF 似乎给了我两个加载数据的选项:

  1. 在初始查询期间一次加载所有子实体(Include如上使用),或
  2. 一次加载一个子实体(使用延迟加载,或显式使用Load/ LoadProperty)。

如果选项 1 有效,它将是我的首选选项,但由于在这种情况下不起作用,我唯一剩下的选项是 2 - 我认为这是不可接受的:输入列表的数据库查询太多ID(即实体的数量)很大。

在我看来,EF 似乎没有解决另一个选项:获取主要实体,获取所有相关的 SubEntity1 实体,然后获取所有相关的 SubEntity2 实体等。这样,查询的数量与要获取的实体类型的数量,而不是实体的数量。这将更好地扩展。

我在 EF 中看不到这样做的方法:换句话说,就是说“为所有这些实体加载此属性(在单个查询中)”。

我是否只需要放弃 EF 并编写自己的 SQL?


更新 我注意到即使我删除了Includes,生成的 SQL 也比我认为的要复杂,我认为这一切都源于 EF 不“喜欢”我的表结构这一事实。为了让 EF 通过 Code First(和 Fluent API)创建我正在寻找的数据库结构,我苦苦挣扎了好几天,即使我(几乎)到达了我想去的地方,我也不得不接受一些妥协。

我想我现在因为敢于做 EF 不希望我做的事情而受到进一步的惩罚。看起来一个简单的查询比它应该的更复杂,一个稍微复杂的查询要复杂得多。

这令人难以置信的沮丧——我以为我已经把所有这些 EF 麻烦抛在脑后了,并且系统现在正在生产中,有几十个用户——这让我很难重新开始。

看来我将不得不在每一个转折点上与 EF 牙齿和指甲进行永恒的战斗。我多么希望我一开始就没有使用它!

无论如何,回到我原来的问题:如果我有一堆 A 类型的实体,我想在一个查询中加载 B 类型的相关子实体,有没有办法做到这一点?

4

1 回答 1

3

如何使用存储过程加载数据?是的,它有点脏,但是当我遇到 EF 的性能问题时,我就是这样做的。我希望我没有遗漏你的问题。

http://msdn.microsoft.com/en-US/data/jj691402

于 2013-08-09T08:39:34.937 回答