Natknąłem się na to:
https://entityframework.codeplex.com/wikipage?title=Przechwytywanie
I wygląda na to, że możesz zrobić coś takiego:
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);
}
}
I zarejestruj to w ten sposób (zrobiłem to w Application_Start
z global.asax.cs
):
DbInterception.Add(new HintInterceptor());
I pozwoli ci zmienić CommandText
. Jedynym problemem jest to, że jest teraz dołączony do każdego zapytanie czytelnika, które może być problemem, ponieważ niektóre z nich mogą mieć negatywny wpływ na tę wskazówkę. Zgaduję, że mogę coś zrobić z kontekstem, aby dowiedzieć się, czy podpowiedź jest odpowiednia, czy nie, lub, co gorsza, mógłbym sprawdzić CommandText
się.
Nie wydaje się najbardziej eleganckim lub drobnoziarnistym rozwiązaniem.
Edytuj :Z interceptorContext
, możesz pobrać DbContexts
, więc zdefiniowałem interfejs, który wygląda tak:
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
A następnie utworzyłem klasę, która wywodzi się z mojego oryginalnego DbContext (wygenerowanego przez EF) i implementuje powyższy interfejs. Następnie zmieniłem myśliwiec tak, aby wyglądał tak:
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);
}
}
Teraz, aby go użyć, tworzę kontekst przy użyciu mojej klasy pochodnej zamiast oryginalnej, ustawiam QueryHint
do czegokolwiek zechcę (recompile
w tym przypadku) i ustaw ApplyHint
tuż przed wykonaniem polecenia, a potem ustawiam je z powrotem na false.
Aby wszystko było bardziej samowystarczalne, zdefiniowałem interfejs w następujący sposób:
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
I rozszerzyłem mój kontekst bazy danych w ten sposób (oczywiście można po prostu użyć częściowej klasy do rozszerzenia klasy generowanej przez EF):
public class MyEntities_Ext : MyEntities, IQueryHintContext
{
public string QueryHint { get; set; }
public bool ApplyHint { get; set; }
}
A potem, aby nieco ułatwić obsługę części włączania i wyłączania, zdefiniowałem to:
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;
}
}
Teraz, aby z niego skorzystać, mogę zrobić tylko to:
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
}
Może to być trochę przesadne i może być dalej rozwijane (na przykład używając wyliczenia dla dostępnych wskazówek zamiast ciągu znaków - lub podklasy recompile
wskazówka zapytania, dzięki czemu nie trzeba określać ciągu recompile
za każdym razem i ryzykować literówkę), ale rozwiązało to mój najpilniejszy problem.