Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Hinzufügen eines Abfragehinweises beim Aufrufen einer Tabellenwertfunktion

Ich bin auf Folgendes gestoßen:

https://entityframework.codeplex.com/wikipage?title=Interception

Und es scheint, dass Sie so etwas tun können:

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);
    }
}

Und registrieren Sie es so (ich habe es in Application_Start gemacht von global.asax.cs ):

DbInterception.Add(new HintInterceptor());

Und Sie können den CommandText ändern . Das einzige Problem ist, dass es jetzt für alle angehängt ist Reader-Abfrage, die ein Problem darstellen könnte, da einige von ihnen durch diesen Hinweis negativ beeinflusst werden könnten. Ich schätze, ich kann etwas mit dem Kontext tun, um herauszufinden, ob der Hinweis angemessen ist oder nicht, oder im schlimmsten Fall könnte ich den CommandText untersuchen selbst.

Scheint nicht ganz die eleganteste oder feinkörnigste Lösung zu sein.

Bearbeiten :Aus dem interceptorContext , können Sie die DbContexts abrufen , also habe ich eine Schnittstelle definiert, die so aussieht:

public interface IQueryHintContext
{
    string QueryHint { get; set; }
    bool ApplyHint { get; set; }
}

Und dann eine Klasse erstellt, die von meinem ursprünglichen DbContext (von EF generiert) abgeleitet ist und die obige Schnittstelle implementiert. Dann habe ich meinen Interceptor so geändert, dass er so aussieht:

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);
    }
}

Um es jetzt zu verwenden, erstelle ich einen Kontext mit meiner abgeleiteten Klasse anstelle des Originals, setze QueryHint zu dem, was ich will (recompile in diesem Fall) und setzen Sie ApplyHint kurz bevor ich den Befehl ausführe und ihn danach wieder auf false setze.

Um das alles ein wenig eigenständiger zu machen, habe ich schließlich eine Schnittstelle wie diese definiert:

public interface IQueryHintContext
{
    string QueryHint { get; set; }
    bool ApplyHint { get; set; }
}

Und erweitert meinen Datenbankkontext wie folgt (Sie könnten natürlich auch einfach eine partielle Klasse verwenden, um die von EF generierte Klasse zu erweitern):

public class MyEntities_Ext : MyEntities, IQueryHintContext
{
    public string QueryHint { get; set; }
    public bool ApplyHint { get; set; }
}

Und dann, um den Ein- und Ausschaltteil ein wenig einfacher zu handhaben, habe ich Folgendes definiert:

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;
    }
}

Um es jetzt zu verwenden, kann ich nur Folgendes tun:

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
}

Dies ist möglicherweise etwas übertrieben und könnte weiterentwickelt werden (z. B. Verwenden einer Aufzählung für verfügbare Hinweise anstelle eines Strings - oder Unterklassen eines recompile Abfragehinweis, sodass Sie die Zeichenfolge recompile nicht angeben müssen jedes Mal und riskiere einen Tippfehler), aber es löste mein unmittelbares Problem.