Narazil jsem na toto:
https://entityframework.codeplex.com/wikipage?title=Interception
A zdá se, že můžete udělat něco takového:
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);
}
}
A zaregistrujte to takto (udělal jsem to v Application_Start
z global.asax.cs
):
DbInterception.Add(new HintInterceptor());
A umožní vám to změnit CommandText
. Jediný problém je, že je nyní připojen pro každý dotaz čtenáře, což může být problém, protože některé z nich mohou být touto nápovědou negativně ovlivněny. Hádám, že mohu něco udělat s kontextem, abych zjistil, zda je nápověda vhodná nebo ne, nebo v horším případě bych mohl prozkoumat CommandText
sám.
Nezdá se to úplně nejelegantnější nebo nejjemnější řešení.
Upravit :Z interceptorContext
, můžete získat DbContexts
, takže jsem definoval rozhraní, které vypadá takto:
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
A pak vytvořil třídu, která pochází z mého původního DbContextu (vygenerovaného EF) a implementuje výše uvedené rozhraní. Pak jsem změnil svůj interceptor, aby vypadal takto:
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);
}
}
Nyní, abych to mohl použít, vytvořím kontext pomocí své odvozené třídy namísto původní, nastavte QueryHint
na cokoliv, co chci, aby to bylo (recompile
v tomto případě) a nastavte ApplyHint
těsně před provedením příkazu a poté jej nastavím zpět na hodnotu false.
Aby to všechno bylo trochu více soběstačné, nakonec jsem definoval rozhraní jako toto:
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
A rozšířil můj kontext db takto (můžete samozřejmě použít jen částečnou třídu k rozšíření třídy vygenerované EF):
public class MyEntities_Ext : MyEntities, IQueryHintContext
{
public string QueryHint { get; set; }
public bool ApplyHint { get; set; }
}
A pak, aby byla manipulace se zapínací a vypínací částí trochu jednodušší, definoval jsem toto:
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;
}
}
Abych to teď mohl použít, mohu udělat jen toto:
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
}
To může být trochu přehnané a mohlo by být dále rozvíjeno (například použitím výčtu pro dostupné rady místo řetězce - nebo podtřídou recompile
dotaz, takže nemusíte zadávat řetězec recompile
pokaždé a riskuji překlep), ale můj okamžitý problém to vyřešilo.