【问题标题】:Entity Framework and SQL Server Profiler实体框架和 SQL Server Profiler
【发布时间】:2013-07-04 14:25:21
【问题描述】:

我在通过 Web 应用程序运行 EF 查询和将 Profiler 生成的 T-SQL 直接运行到 SQL 查询窗口之间存在一些性能测量问题。

以下是我通过 Web 应用程序执行的 EF 查询:

IEnumerable<application> _entityList = context.applications
                    .Include(context.indb_generalInfo.EntitySet.Name)
                    .Include(context.setup_budget.EntitySet.Name)
                    .Include(context.setup_committee.EntitySet.Name)
                    .Include(context.setup_fund.EntitySet.Name)
                    .Include(context.setup_appStatus.EntitySet.Name)
                    .Include(context.appSancAdvices.EntitySet.Name)
                    .Where(e => e.indb_generalInfo != null);

                if (isIFL != null)
                    _entityList = _entityList.Where(e => e.app_isIFL == isIFL);

                int _entityCount = _entityList.Count(); // hits the database server at this line

在 SQL Profiler 中跟踪上述 EF 查询时,它显示执行大约需要 221'095 毫秒。 (applications 表有 30,000 多条记录,indb_generalInfo 有 11,000 多条记录,appSancAdvices 有 30,000 多条记录)。

但是,当我从 Profiler 复制 T-SQL 并直接从查询窗口运行它时,只需要大约 4'000 毫秒。

为什么会这样?

【问题讨论】:

  • 您是否尝试过多次运行相同的查询? Entity Framework 仍然需要生成该查询,但它将被缓存以供将来的请求使用。此外,如果您正在执行Count(),则无需预先加载。
  • 是的,我之前尝试过。但问题正是@Arnold 通知的。

标签: c# performance entity-framework sql-server-profiler


【解决方案1】:

此查询中的毒液在第一个单词中:IEnumerable&lt;application&gt;。如果您将其替换为var(即IQueryable),则查询将被翻译成SQL,直到并包括最后一个Count()。这将花费更少的时间,因为传输的数据量几乎减少到零。

此外,正如 bobek 已经提到的,您不需要 Includes,因为您只计算 context.applications 项目。

除此之外,您总是会注意到使用像实体框架这样的 ORM 的开销。

【讨论】:

  • IQueryable 的 EF 实现使用延迟执行,因此,正如您所说,在调用 Count() 之前,查询不会转换为 sql。您还可以调用 AsEnumerable() 或 ToList() 来强制转换,这在您开始使用无法转换为 sql 的过滤器时很有用。但是 AFAIK 只是将变量声明从 IEnumerable 更改为 IQueryable 不会有任何区别。
  • @Colin 会的。使用IEnumerable&lt;application&gt; 与在第一条语句之后调用AsEnumerable 具有相同的效果,因此在应用其余的LINQ 语句之前强制将记录抓取到内存中,即使查询的执行被推迟到@ 987654329@.
  • 我在我的代码中测试了这一点,方法是将变量声明从 var 更改为 IEnumerable 并从如果传递到 EF 时失败的查询中删除对 AsEnumerable() 的调用。我可以确认@Gert 是正确的。我是正确的,今天我学到了一些新东西。所以问题中的评论说它“在这一行命中数据库服务器”是不正确的,从 SQL Profiler 中查看的查询中应该很明显 - 结果将是结果集而不是整数
  • 等一下 - 数据库将在 Count 处命中 - 但数据库查询将返回结果集,并且计数将在内存中完成,对吗?
  • 格特阿诺德!这真的很重要。我猜由于 IEnumerable,它在点击 .Count() 时将 30k+ 应用程序对象转换为可枚举。是吗? @Colin,为什么你之前一直说查询没有在 .Count() 访问服务器?
【解决方案2】:

那是因为 EF 需要先将您的代码翻译成 TSQL,这也很昂贵。在此处查看此链接:http://peterkellner.net/2009/05/06/linq-to-sql-slow-performance-compilequery-critical/ 它可以让您编译 LINQ,并且可以帮助您提高速度。另外,你真的需要这么多表来进行这个查询吗?或许你可以想办法把它过滤掉,只取出你需要的东西?

【讨论】:

  • 因为 ef5 现在应该默认缓存
【解决方案3】:

EF 在性能方面肯定是有代价的。但它也为复杂的 TSQL 提供了使用存储过程的灵活性。但在我看来,这应该是你最后的手段。

【讨论】:

    【解决方案4】:

    如果您对性能和 EF 感兴趣。 http://msdn.microsoft.com/en-us/data/hh949853.aspx

    不过……

    SQL Profiler 中的 EF 查询显示它需要大约 221'095 毫秒 执行。

    那么..

    从 Profiler 复制 T-SQL 并直接从查询窗口运行它

    SQL 的来源无关紧要。 Q1 花了 x 毫秒。基于 SQL 探查器信息 完全相同的查询 Q1' 基于 SQL 探查器需要更少。这意味着SQL的来源不是问题,这意味着涉及环境问题。

    最明显的解释是,SQL Server 缓冲了很多数据页,可以更好地服务第二个相同的请求。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-29
      • 1970-01-01
      • 2010-11-04
      • 1970-01-01
      • 1970-01-01
      • 2012-01-06
      • 1970-01-01
      相关资源
      最近更新 更多