Jeg stødte på dette:
https://entityframework.codeplex.com/wikipage?title=Interception
Og det ser ud til, at du kan gøre noget som dette:
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);
}
}
Og registrer det sådan her (jeg gjorde det i Application_Start
af global.asax.cs
):
DbInterception.Add(new HintInterceptor());
Og det vil lade dig ændre CommandText
. Det eneste problem er, at det nu er vedhæftet til hver læserforespørgsel, som kan være et problem, da nogle af dem kan blive negativt påvirket af dette tip. Jeg gætter på, at jeg kan gøre noget med konteksten for at finde ud af, om hintet er passende eller ej, eller i værre tilfælde kunne jeg undersøge CommandText
sig selv.
Virker ikke helt den mest elegante eller finkornede løsning.
Rediger :Fra interceptorContext
, kan du få DbContexts
, så jeg definerede en grænseflade, der ser sådan ud:
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
Og så oprettede en klasse, der stammer fra min originale DbContext (genereret af EF) og implementerer ovenstående grænseflade. Så ændrede jeg min interceptor til at se sådan ud:
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);
}
}
For nu at bruge det, opretter jeg en kontekst ved hjælp af min afledte klasse i stedet for originalen, sæt QueryHint
til hvad jeg vil have det til at være (recompile
i dette tilfælde) og indstil ApplyHint
lige før jeg udfører kommandoen og sætter den tilbage til falsk bagefter.
For at gøre det hele lidt mere selvstændigt, endte jeg med at definere en grænseflade som denne:
public interface IQueryHintContext
{
string QueryHint { get; set; }
bool ApplyHint { get; set; }
}
Og udvidede min db-kontekst sådan her (du kunne selvfølgelig bare bruge en delvis klasse til også at udvide den EF-genererede klasse):
public class MyEntities_Ext : MyEntities, IQueryHintContext
{
public string QueryHint { get; set; }
public bool ApplyHint { get; set; }
}
Og så, for at gøre tænd- og sluk-delen lidt nemmere at håndtere, definerede jeg dette:
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;
}
}
For nu at bruge det, kan jeg gøre netop dette:
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
}
Dette er måske lidt overdrevet og kunne udvikles yderligere (for eksempel ved at bruge en enum for tilgængelige tip i stedet for en streng - eller underklasser en recompile
forespørgselstip, så du ikke behøver at angive strengen recompile
hver gang og risikere en tastefejl), men det løste mit umiddelbare problem.