Entity framework 在调用表值函数时添加查询提示
我正在从entity framework调用一个表值函数,需要能够向其中添加Entity framework 在调用表值函数时添加查询提示,entity-framework,sql-server-2012,Entity Framework,Sql Server 2012,我正在从entity framework调用一个表值函数,需要能够向其中添加选项(重新编译),因为它选择的执行计划不是最优的。在SQL Server Management Studio中运行查询时,其外观如下所示: select * from dbo.fDE_myquery(0, 0, 3309, '7/1/2013', '7/1/2014', 0, 0) option (recompile) public class HintInterceptor : DbCommandI
选项(重新编译)
,因为它选择的执行计划不是最优的。在SQL Server Management Studio中运行查询时,其外观如下所示:
select
*
from dbo.fDE_myquery(0, 0, 3309, '7/1/2013', '7/1/2014', 0, 0)
option (recompile)
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);
}
}
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
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);
}
}
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
根据EF的说法,没有办法添加这个提示,阿福。EF部分看起来像:
var query = from f in ctx.fDE_myQuery(aBool, anotherBool, StartDate,
EndDate, someInt, moreBool)
select f;
我看到这个问题:
但它很旧,而且公认的解决方案并没有提供足够的信息,说明如何使用实体框架实际实现建议的解决方案(使用计划指南)。如果这是唯一的解决方案,您如何让entity framework使用计划指南?我遇到了以下问题:
看起来你可以这样做:
select
*
from dbo.fDE_myquery(0, 0, 3309, '7/1/2013', '7/1/2014', 0, 0)
option (recompile)
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);
}
}
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
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);
}
}
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
它将允许您更改命令文本
。唯一的问题是,它现在附加到每个读者查询,这可能是一个问题,因为其中一些可能会受到该提示的负面影响。我猜我可以通过上下文来判断提示是否正确,或者更糟的是,我可以检查CommandText
本身
似乎不是最优雅或细粒度的解决方案
Edit:从interceptorContext
,您可以获得DbContexts
,因此我定义了一个如下所示的界面:
select
*
from dbo.fDE_myquery(0, 0, 3309, '7/1/2013', '7/1/2014', 0, 0)
option (recompile)
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);
}
}
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
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);
}
}
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
然后创建了一个从原始DbContext(由EF生成)派生的类,并实现了上述接口。然后我将拦截器更改为如下所示:
select
*
from dbo.fDE_myquery(0, 0, 3309, '7/1/2013', '7/1/2014', 0, 0)
option (recompile)
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);
}
}
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
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);
}
}
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
并像这样扩展了我的db上下文(当然,您也可以使用分部类来扩展EF生成的类):
然后,为了使打开、关闭部分更易于处理,我定义了以下内容:
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;
}
}
现在要使用它,我可以这样做:
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
}
这可能有点过火,可以进一步开发(例如,使用枚举作为可用提示,而不是字符串-或子类化
recompile
query提示,这样您就不需要每次都指定字符串recompile
,从而冒着键入错误的风险),但它解决了我眼前的问题。在您的特定用法之外,是否还有其他的fDE\u myquery
调用方?多久打一次电话?问题不是您的SELECT*FROM dbo.fDE_myquery()
正在获得次优计划,即fDE_myquery
中的一个或多个查询正在获得次优计划。因此,您可以将选项(重新编译)
添加到该TVF中的一个或多个查询中
如果这个TVF被称为很多,那么这将对性能产生负面影响。这就是为什么我问起这个TVF的其他用途:如果这是这个TVF的唯一用途,或者说是到目前为止的主要用途,那么如果这些糟糕的计划经常被发现,那么它可能是值得的
但是,如果此TVF的其他几个调用者没有遇到问题,则将重新编译
放入TVF中可能不是一个好办法。不过,在这种情况下,您可以创建一个包装器TVF来封装SELECT*FROM dbo.fDE_myquery()选项(重新编译)代码>。这似乎是一个更灵活的解决方案:)。它必须是一个多状态TVF,而不是通常更好的内联TVF,因为我刚刚尝试过它,内联TVF似乎不喜欢选项
子句,但多状态TVF对它很好
编辑:
或者,如果您想纯粹在EF中处理此问题,只需使用一行代码发出重新编译请求:
ctx.context.ExecuteStoreCommand("EXEC sp_recompile 'dbo.fDE_myquery';");
然后做你的:
我确信我可以在TVF中进行拼写(我没有写),甚至把TVF包裹在另一个TVF中,只是用所需的查询提示调用它,但我认为这是最后的办法。似乎应该有一种方法可以在EF中指定一个查询提示,而不必更改数据库中的内容。@MattBurland:这很公平。我刚刚有了另一个想法,我更新了我的答案。有不止一种方法可以获得重新编译
:)。您能详细解释一下您是如何调用它的吗?添加选项(重新编译)
花费了>10秒的linq查询,我已经处理了几天了,一直到@drexdrex:我扩展了一点。希望能有所帮助。我要特别感谢@MattBurland提供的这种清洁解决方案。我根据你的拦截器调整了我的项目,现在我对执行时间非常满意。Briliant解决方案!!我提到过。