【问题标题】:Optimize Entity framework Query, avoid lazy-loading优化实体框架查询,避免延迟加载
【发布时间】:2015-04-26 08:21:59
【问题描述】:

我有一个 linq 查询需要几秒钟(~2.6 秒)才能运行。但我想尽量减少。

我只需要阅读,所以我已经包含了 .AsNoTracking() 行为。

我还测试了没有包含语句的查询,但是我在获取请求后的操作进一步减慢了它,所以我留下了包含以优化我的其他操作。

主要目标是减少数据库对话,因此 ToList(),Include 语句。

代码:

var obj = _context.MyContextModel.AsNoTracking()
            .Where(x => x.CategoryList.Model.Id == 1)
            .Where(x => x.CategoryList.Model.TypeId == 1)
            .Where(x => x.Year.Select(y=>y.Datetime).Any(item => item.Year == 2010))
            .Include(x => x.LinkedMarket).AsNoTracking()
            .Include(x => x.Year).AsNoTracking()
            .Include(x => x.CategoryList).AsNoTracking()
            .Include(x => x.CategoryList.Model).AsNoTracking();

return obj.AsParallel().ToList();

此操作通常返回大约 1000-2000 条 MyContextModel 记录,不包括“包含”

如何进一步优化? 我应该将对象加载到容器类吗?还是其他解决方案?

更新

_context.Configuration.ProxyCreationEnabled = false;
_context.Configuration.LazyLoadingEnabled = false;
var obj = _context.MyContextModel.AsNoTracking()
                .Where(x => x.CategoryList.Model.Id == 1)
                .Where(x => x.CategoryList.Model.TypeId == 1)
                .Where(x => x.LinkedMarket.FirstOrDefault(mar=>mar.MarketID == marketId) != null)
                .Include(x => x.Year).AsNoTracking()
                .Include(x => x.CategoryList).AsNoTracking()
                .Include(x => x.CategoryList.Model).AsNoTracking();

return obj.AsParallel().ToList();

基本上我已经删除了过滤年份的 where 子句(我稍后会这样做,因此包括年份) 我添加了一个 Where 子句,从一开始就指定市场。

我已删除包含市场的包含。

链接市场是一个大的性能窃贼(我不知道为什么,这是 EF 不喜欢的东西。)

这将查询减少到大约平均 0.4 秒。 整个操作从 4+ 秒到惊人的 0.7 秒。

【问题讨论】:

  • 您是否在 SSMS 中分析了生成的查询以查找缺失索引等?
  • 某事告诉我问题就在那里。Where(x => x.Year.Select(y=>y.Datetime).Any(item => item.Year == 2010))
  • @ErikEJ 。我无法控制数据库,所以无论如何我都无法更改任何内容。
  • 你有很多AsNoTracking()。尝试使用_context.Configuration.ProxyCreationEnabled = false;Include(x => x.CategoryList.Model) 没用。使用Include("CategoryList.Model")。否则,实现是有代价的。
  • 您需要所有列吗?您可以通过预测获得很多收益。

标签: c# performance linq entity-framework


【解决方案1】:

您所做的每个包含最终都会在数据库中执行一个连接。 假设您的左表非常大,记录大小为 1024 字节,并且您有很多详细信息,例如 1000,并且详细记录大小只有 100。 这将导致左表的信息重复 1000 次,此信息将由 db 在线传输,EF 必须过滤掉重复的内容以创建您的左实例。

最好不要使用包含并进行显式加载。基本上在同一个上下文中执行 2 个查询。

我在下面有一个使用此原理的示例。它可以比依赖包含快 10 倍。 (一个数据库只能有效地处理有限数量的连接)

var adressen = adresRepository
                .Query(r => r.RelatieId == relatieId)
                .Include(i => i.AdresType)
                .Select().ToList();

var adresids = (from a in adressen select a.AdresId).ToList();
            IRepositoryAsync<Comm> commRepository = unitOfWork.RepositoryAsync<Comm>();

            var comms = commRepository
                .Query(c => adresids.Contains(c.AdresId))
                .Include(i => i.CommType)
                .Select();

对于我使用包含的 commType 和 adresType,因为存在一对一的关系,我避免了太多的连接,因此我的多个查询将比使用包含的单个查询更快。我没有在第一个查询中包含 Comms 来尝试避免第二个查询,关键是在这种情况下 2 个查询比单个查询要快。

最重要的是,除了避免延迟加载之外,还有更多需要考虑的问题,还需要考虑哪些包含需要哪些不需要。您可能需要这些信息,并且包含快速且简单,但在相同上下文中的额外查询会更快。

【讨论】:

  • 在我看来,EF 现在应该足够健壮,以至于人们不必编写多个查询来及时返回数据。这增加了更多的维护麻烦。
  • @Kix,EF 不会取代需要了解现有机制并需要运用常识的谦虚开发人员。包含通常作为连接处理,这会产生后果。 EF 不能以任何其他方式做到这一点,因为它必须知道数据。
  • 因此,作为设计师,您可以使用延迟加载、急切加载或显式加载。设计师决定并对其查询运行性能测试。
  • 我同意,如果时间间隔很长,那么也许他应该考虑使用 sproc。这对 EF 来说有点问题,因为我遇到了同样的问题。对我来说,编写一个存储过程并通过 EF 调用它更便宜。
  • @PhilipStuyck 所说的基础:EF 在设计上与生成的 SQL 尽可能接近一对一;您编写错误的 EF 查询和编写错误的 SQL 查询的可能性一样大。出于这个原因,如果您足够努力,EF 中的良好查询(结合数据库上的索引)通常与存储过程一样有效。说到这一点,更多的开发人员需要知道他们的 EF 查询的 IQueryable.ToString() 结果。
猜你喜欢
  • 2012-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多