EF 6参数嗅探

我有一个动态查询,太大了,无法放在这里。 可以肯定地说,在它的当前forms中,它利用CLR过程根据传递的搜索参数的数量动态地构建连接,然后获取该结果并将其连接到更详细的表以带回对最终用户重要的属性。 我已经将整个查询转换为LINQ to Entities,我发现它生成的SQL足以完成这项工作,无论是通过EF 6运行,查询超时。 获取生成的SQL并在SSMS中运行它在3秒或更短的时间内运行。 我只能想象我的问题是参数嗅探。 我已经尝试更新数据库中每个表的统计信息,但这还没有解决问题。

我的问题是:

我可以通过EF以某种方式嵌入像“OPTION RECOMPILE”这样的选项吗?

在DB上执行它们之前,可以使用EF6的拦截function来操作其内部SQL命令,例如在命令末尾添加option(recompile)

 public class OptionRecompileHintDbCommandInterceptor : IDbCommandInterceptor { public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { addQueryHint(command); } public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { addQueryHint(command); } private static void addQueryHint(IDbCommand command) { if (command.CommandType != CommandType.Text || !(command is SqlCommand)) return; if (command.CommandText.StartsWith("select", StringComparison.OrdinalIgnoreCase) && !command.CommandText.Contains("option(recompile)")) { command.CommandText = command.CommandText + " option(recompile)"; } } } 

要使用它,请在应用程序的开头添加以下行:

 DbInterception.Add(new OptionRecompileHintDbCommandInterceptor()); 

我有类似的问题。 最后,我使用以下命令删除了缓存的查询计划:

 dbcc freeproccache([your plan handle here]) 

为了获得您的计划处理,您可以使用以下查询:

 SELECT qs.plan_handle, a.attrlist, est.dbid, text FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) est CROSS APPLY (SELECT epa.attribute + '=' + convert(nvarchar(127), epa.value) + ' ' FROM sys.dm_exec_plan_attributes(qs.plan_handle) epa WHERE epa.is_cache_key = 1 ORDER BY epa.attribute FOR XML PATH('')) AS a(attrlist) WHERE est.text LIKE '%standardHourRate%' and est.text like '%q__7%'and est.text like '%Unit Overhead%' AND est.text NOT LIKE '%sys.dm_exec_plan_attributes%' 

用适当的查询部分替换’like’子句的内容。

你可以看到我的整个问题:

使用entity framework的SQL查询运行速度较慢,使用错误的查询计划

我喜欢VahidN的解决方案,投票给他,但我希望能更好地控制它何时发生。 事实certificate,DB Interceptor是非常全局的,我只希望在特定场景中的特定情境下发生这种情况。

在这里,我们正在设置基础工作,以支持添加其他查询提示,可以根据需要打开和关闭。

由于我经常公开传递连接字符串的方法,因此我也包括了对它的支持。

下面将为您的上下文提供一个标志,通过扩展EF生成的部分类来以编程方式启用/禁用提示。 我们还将Interceptor中的一小部分重用代码扔进了自己的方法中。

小界面

 public interface IQueryHintable { bool HintWithRecompile { get; set; } } 

DB命令拦截器

 public class OptionHintDbCommandInterceptor : IDbCommandInterceptor { public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { AddHints(command, interceptionContext); } public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { AddHints(command, interceptionContext); } public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { AddHints(command, interceptionContext); } private static void AddHints(DbCommand command, DbCommandInterceptionContext interceptionContext) { var context = interceptionContext.DbContexts.FirstOrDefault(); if (context is IQueryHintable) { var hints = (IQueryHintable)context; if (hints.HintWithRecompile) { addRecompileQueryHint(command); } } } private static void addRecompileQueryHint(IDbCommand command) { if (command.CommandType != CommandType.Text || !(command is SqlCommand)) return; if (command.CommandText.StartsWith("select", StringComparison.OrdinalIgnoreCase) && !command.CommandText.Contains("option(recompile)")) { command.CommandText = command.CommandText + " option(recompile)"; } } } 

扩展实体上下文以添加IQueryHintable

 public partial class SomeEntities : DbContext, IQueryHintable { public bool HintWithRecompile { get; set; } public SomeEntities (string connectionString, bool hintWithRecompile) : base(connectionString) { HintWithRecompile = hintWithRecompile; } public SomeEntities (bool hintWithRecompile) : base() { HintWithRecompile = hintWithRecompile; } public SomeEntities (string connectionString) : base(connectionString) { } } 

注册DB命令拦截器(global.asax)

  DbInterception.Add(new OptionHintDbCommandInterceptor()); 

启用上下文范围

  using(var db = new SomeEntities(hintWithRecompile: true) ) { } 

打开或关闭

  db.HintWithRecompile = true; // Do Something db.HintWithRecompile = false; 

我调用了这个HintWithRecompile,因为您可能还想实现HintOptimizeForUnknown或其他查询提示。

对于我和@Greg一样,启用此系统并不是一个选项,因此我编写了这个小实用程序类,可以临时为OptionRecompileScope中执行的查询添加选项(重新编译)。

用法示例

 using (new OptionRecompileScope(dbContext)) { return dbContext.YourEntities.Where().ToList(); } 

履行

 public class OptionRecompileScope : IDisposable { private readonly OptionRecompileDbCommandInterceptor interceptor; public OptionRecompileScope(DbContext context) { interceptor = new OptionRecompileDbCommandInterceptor(context); DbInterception.Add(interceptor); } public void Dispose() { DbInterception.Remove(interceptor); } private class OptionRecompileDbCommandInterceptor : IDbCommandInterceptor { private readonly DbContext dbContext; internal OptionRecompileDbCommandInterceptor(DbContext dbContext) { this.dbContext = dbContext; } public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { if (ShouldIntercept(command, interceptionContext)) { AddOptionRecompile(command); } } public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { if (ShouldIntercept(command, interceptionContext)) { AddOptionRecompile(command); } } public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { } private static void AddOptionRecompile(IDbCommand command) { command.CommandText = command.CommandText + " option(recompile)"; } private bool ShouldIntercept(IDbCommand command, DbCommandInterceptionContext interceptionContext) { return command.CommandType == CommandType.Text && command is SqlCommand && interceptionContext.DbContexts.Any(interceptionDbContext => ReferenceEquals(interceptionDbContext, dbContext)); } } }