Ответ 1
Я наткнулся на это:
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);
}
}
И зарегистрируйте его так (я сделал это в Application_Start
of global.asax.cs
):
DbInterception.Add(new HintInterceptor());
И это позволит вам изменить CommandText
. Единственная проблема заключается в том, что он теперь привязан для каждого запроса читателя, который может быть проблемой, поскольку некоторые из них могут быть негативно затронуты этим намеком. Я предполагаю, что могу что-то сделать с контекстом, чтобы выяснить, подходит ли подсказка или нет, или, что еще хуже, я мог бы рассмотреть сам CommandText
.
Не кажется, что это самое элегантное или тонкое решение.
Изменить. Из interceptorContext
вы можете получить DbContexts
, поэтому я определил интерфейс, который выглядит следующим образом:
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
И затем создайте класс, который происходит из моего исходного 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);
}
}
Теперь, чтобы использовать его, я создаю контекст с использованием моего производного класса вместо оригинала, устанавливаю QueryHint
во все, что я хочу (recompile
в этом случае), и устанавливаю ApplyHint
прямо перед тем, как выполнить и снова установите его на значение false.
Чтобы сделать это все более самодостаточным, я в конечном итоге определил такой интерфейс:
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
И расширил мой контекст db как это (вы могли бы, конечно, просто использовать частичный класс для расширения класса, сгенерированного EF):
public class MyEntities_Ext : MyEntities, IQueryHintContext
{
public string QueryHint { get; set; }
public bool ApplyHint { get; set; }
}
И затем, чтобы сделать включение, отключение части немного легче обрабатывать, я определил это:
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
), поэтому вам не нужно указывать строку recompile
каждый раз и рисковать опечаткой), но он решил мою непосредственную проблему.