如何优化我的EF Code First查询?

时间:2013-08-08 14:15:07

标签: entity-framework ef-code-first entity-framework-5

[已更新 - 请参阅底部的更新]

我首先使用的是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之外,它不是一个特别复杂的查询。

EF为此生成的SQL 巨大 - 围绕 74Kb 的SQL。执行不需要很长时间(因为通常ID列表中的项目数量很少),但只需构建查询就需要E​​F 一秒钟 - 即查询之前甚至被发送到数据库。

如果我删除Includes,那么查询要小得多,而且整个过程花费的时间要少得多 - 但是各个相关的实体会一次一个地加载,这不会扩展好。

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

  1. 在初始查询期间一次加载所有子实体(如上所述使用Include)或
  2. 一次加载子实体(使用延迟加载,或明确使用Load / LoadProperty)。
  3. 选项1将是我的首选选项,如果它有效,但由于在这种情况下不起作用,我唯一剩下的选项是2 - 我不认为这是可以接受的:会有太多的数据库查询输入ID列表(即实体数量)很大。

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

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

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


    更新 我注意到即使我删除了Include,生成的SQL也比我想象的要复杂得多,而且我认为这都源于EF不喜欢我的表结构这一事实。我花了好几天的时间让EF通过Code First(以及Fluent API)创建我想要的数据库结构,即使我必须(几乎)到达我想要的地方,我也不得不接受一些妥协。 / p>

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

    令人难以置信的令人沮丧 - 我以为我已经把所有那些EF麻烦都抛在后面了,而且这个系统现在正在生产中,有很多用户 - 这将使我很难重新开始。

    似乎我将不得不花费永恒的力量与EF牙齿和指甲搏斗。我多么希望我从来没有使用过它!

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

1 个答案:

答案 0 :(得分:3)

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

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