【问题标题】:Entity Framework query slow, but same SQL in SqlQuery is fastEntity Framework 查询慢,但 SqlQuery 中相同的 SQL 快
【发布时间】:2025-11-27 14:45:02
【问题描述】:

我看到一些非常奇怪的性能与使用 .NET 框架版本 4 的 Entity Framework Code-First 的非常简单的查询相关。LINQ2Entities 查询如下所示:

 context.MyTables.Where(m => m.SomeStringProp == stringVar);

这需要 3000 多毫秒才能执行。生成的 SQL 看起来很简单:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = '1234567890'

此查询在通过 Management Studio 运行时几乎立即运行。当我更改 C# 代码以使用 SqlQuery 函数时,它会在 5-10 毫秒内运行:

 context.MyTables.SqlQuery("SELECT [Extent1].[ID] ... WHERE [Extent1].[SomeStringProp] = @param", stringVar);

因此,完全相同的 SQL,生成的实体在两种情况下都被更改跟踪,但两者之间的性能差异很大。什么给了?

【问题讨论】:

  • 我预计您会看到初始化延迟 - 可能是查看编译。请参阅 MSDN:Performance Considerations for Entity Framework 5
  • 我尝试过预生成视图,但似乎没有帮助。此外,在慢速查询之前运行另一个 EF 查询以排除初始化内容。新查询运行得很快,慢的查询仍然运行得很慢,即使在第一次查询期间发生了上下文预热。
  • @marc_s - 不,SqlQuery 将返回一个完全物化并跟踪更改的实体实例。见msdn.microsoft.com/en-us/library/…
  • 为您的 EF 查询生成的 SQL 实际上是内联参数值,还是使用参数?这不应该影响单个查询的查询速度,但可能会随着时间的推移导致服务器中的查询计划膨胀。
  • 您是否尝试过两次/多次运行相同的查询?第二次运行需要多长时间?您是否在 .NET Framework 4.5 上尝试过 - .NET Framework 4.5 中有一些与 EF 相关的性能改进可能会有所帮助。

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


【解决方案1】:

找到了。事实证明这是 SQL 数据类型的问题。数据库中的 SomeStringProp 列是 varchar,但 EF 假定 .NET 字符串类型是 nvarchar。在查询数据库进行比较期间产生的翻译过程需要很长时间。我认为 EF Prof 在这里让我误入歧途,正在运行的查询的更准确表示如下:

 SELECT [Extent1].[ID], [Extent1].[SomeStringProp], [Extent1].[SomeOtherProp],
 ...
 FROM [MyTable] as [Extent1]
 WHERE [Extent1].[SomeStringProp] = N'1234567890'

因此得到的修复是注释代码优先模型,指示正确的 SQL 数据类型:

public class MyTable
{
    ...

    [Column(TypeName="varchar")]
    public string SomeStringProp { get; set; }

    ...
}

【讨论】:

  • 很好的调查。您的查询受到“隐式转换”的影响,如下所述:brentozar.com/archive/2012/07/…
  • 为我节省了几个小时的调试时间。这正是问题所在。
  • 就我而言,我将 EDMX 与旧数据库一起使用,该数据库对所有内容都使用 varchar,这确实是问题所在。我想知道我是否可以制作一个 EDMX 来为所有字符串列考虑 varchar。
  • 伟大的发现人。但是@Jaime 我们应该为数据库优先方法做些什么,因为在从数据库更新 EF 模型后,一切(例如数据库模型上的数据注释)都会消失。
  • 暂时将此设置为我的主页,这样我就可以在一段时间内重温再次找到这么好的答案的兴奋。谢谢!!!
【解决方案2】:

减慢我的 EF 查询的原因是将不可为空的标量与可空的标量进行比较:

long? userId = 10; // nullable scalar

db.Table<Document>().Where(x => x.User.Id == userId).ToList() // or userId.Value
                                ^^^^^^^^^    ^^^^^^
                                Type: long   Type: long?

该查询耗时 35 秒。但是像下面这样的一个小重构:

long? userId = 10;
long userIdValue = userId.Value;

db.Table<Document>().Where(x => x.User.Id == userIdValue).ToList()
                                ^^^^^^^^^    ^^^^^^^^^^^
                                Type: long   Type: long

提供了令人难以置信的结果:只需要 50 毫秒即可完成。它看起来像 EF 中的一个错误。

【讨论】:

  • 这太奇怪了
  • 天啊。当使用接口 IUserId.Id 导致我出现问题时,这显然也会发生,但首先将 Id 映射到整数工作......我现在是否必须检查我的 100.000 行应用程序中的所有查询?
  • 这个错误被报告了吗?它仍然在最新版本 6.2.0
  • EF Core 中也存在同样的问题。感谢您找到这个!
  • 另一个建议是在放入 LINQ 表达式之前处理变量。否则生成的sql会更长更慢。我在 LINQ 表达式中使用 Trim() 和 ToLower() 时遇到过,这让我很恼火。
【解决方案3】:

如果你使用的是流畅的映射,你可以使用IsUnicode(false)作为配置的一部分来获得同样的效果-

http://msdn.microsoft.com/en-us/data/jj591617.aspx#1.9

http://msdn.microsoft.com/en-us/library/gg696416%28v=vs.103%29.aspx

【讨论】:

    【解决方案4】:

    我遇到了同样的问题(从 SQL 管理器执行时查询很快)但是从 EF 执行时超时到期。

    原来实体(从视图创建)有错误的实体键。所以实体有相同键的重复行,我猜它必须在后台进行分组。

    【讨论】:

      【解决方案5】:

      我也遇到过这个复杂的 ef 查询。 对我来说,将 6 秒 ef 查询减少到它生成的亚秒 sql 查询的一个修复方法是关闭延迟加载。

      要找到此设置 (ef 6),请转到 .edmx 文件并查看属性 -> 代码生成 -> 启用延迟加载。设置为假。

      对我来说性能有了很大的提高。

      【讨论】:

      • 这很酷,但与海报问题无关。
      【解决方案6】:

      我也有这个问题。事实证明,我的罪魁祸首是 SQL-Server parameter sniffing

      我的问题实际上是由于参数嗅探引起的第一个线索是,使用“set arithabort off”或“set arithabort on”运行查询会在 Management Studio 中产生截然不同的执行时间。这是因为 ADO.NET 默认使用“set arithabort off”,而 Management Studio 默认使用“set arithabort on”。查询计划缓存根据这个参数保持不同的计划。

      我为查询禁用了查询计划缓存,解决方案你可以找到here

      【讨论】: