调用表值函数时添加查询提示

Mat*_*and 21 entity-framework sql-server-2012

我正在从实体框架调用一个表值函数,并且需要能够添加option (recompile)它,因为它拾取的执行计划不是最佳的.在SQL Server Management Studio中运行查询,它看起来像这样:

select 
       * 
from dbo.fDE_myquery(0, 0, 3309, '7/1/2013', '7/1/2014', 0, 0)
option (recompile)
Run Code Online (Sandbox Code Playgroud)

来自EF,没有办法添加这个提示,AFAIK.EF部分看起来像:

var query = from f in ctx.fDE_myQuery(aBool, anotherBool, StartDate, 
            EndDate, someInt, moreBool)
            select f;
Run Code Online (Sandbox Code Playgroud)

我看到了这个问题:

如何在实体框架中控制参数嗅探和/或查询提示?

但它已经过时了,所接受的解决方案并没有提供足够的信息来说明如何使用实体框架实际实施建议的解决方案(使用计划指南).如果这是唯一的解决方案,那么如何让实体框架使用计划指南呢?

Mat*_*and 40

我遇到了这个:

https://entityframework.codeplex.com/wikipage?title=Interception

看来你可以这样做:

public class HintInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
    {
        command.CommandText += " option (recompile)";
        base.ReaderExecuting(command, interceptionContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

并注册它(我做Application_Startglobal.asax.cs):

DbInterception.Add(new HintInterceptor());
Run Code Online (Sandbox Code Playgroud)

它会让你改变CommandText.唯一的问题是它现在附加了每个读者查询,这可能是一个问题,因为其中一些可能会受到该提示的负面影响.我猜我可以用上下文做一些事情来弄清楚提示是否合适,或者更糟糕的是我可以检查CommandText自己.

似乎不是最优雅或细粒度的解决方案.

编辑:从中interceptorContext,你可以得到DbContexts,所以我定义了一个如下所示的界面:

public interface IQueryHintContext
{
    string QueryHint { get; set; }
    bool ApplyHint { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后创建一个派生自我的原始DbContext(由EF生成)并实现上述接口的类.然后我改变我的拦截器看起来像这样:

public class HintInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
    {
        if (interceptionContext.DbContexts.Any(db => db is Dal.IQueryHintContext))
        {
            var ctx = interceptionContext.DbContexts.First(db => db is Dal.IQueryHintContext) as Dal.IQueryHintContext;
            if (ctx.ApplyHint)
            {
                command.CommandText += string.Format(" option ({0})", ctx.QueryHint);
            }
        }
        base.ReaderExecuting(command, interceptionContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在使用它,我使用我的派生类而不是原始类创建一个上下文,设置QueryHint为我想要的任何内容(recompile在这种情况下)并ApplyHint在我执行命令之前设置并在之后将其设置为false.

为了使这一切更加独立,我最终定义了一个这样的界面:

public interface IQueryHintContext
{
    string QueryHint { get; set; }
    bool ApplyHint { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

并像这样扩展我的db上下文(当然,你可以使用一个partial类来扩展EF生成的类):

public class MyEntities_Ext : MyEntities, IQueryHintContext
{
    public string QueryHint { get; set; }
    public bool ApplyHint { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后,为了使开启,关闭部分更容易处理,我定义了这个:

public class HintScope : IDisposable
{
    public IQueryHintContext Context { get; private set; }
    public void Dispose()
    {
        Context.ApplyHint = false;
    }

    public HintScope(IQueryHintContext context, string hint)
    {
        Context = context;
        Context.ApplyHint = true;
        Context.QueryHint = hint;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在使用它,我可以做到这一点:

using (var ctx = new MyEntities_Ext()) 
{
    // any code that didn't need the query hint
    // ....
    // Now we want the query hint
    using (var qh = new HintScope(ctx, "recompile"))
    {
        // query that needs the recompile hint
    }
    // back to non-hint code
}
Run Code Online (Sandbox Code Playgroud)

这可能有点过度,可能会进一步发展(例如,使用枚举可用提示而不是字符串 - 或者对recompile查询提示进行子类化,这样您就不需要recompile每次都指定字符串并冒着错误的风险),但它解决了我的直接问题.