Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Aggiunta di un suggerimento per la query quando si chiama la funzione con valori di tabella

Mi sono imbattuto in questo:

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

E sembra che tu possa fare qualcosa del genere:

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

E registralo in questo modo (l'ho fatto in Application_Start di global.asax.cs ):

DbInterception.Add(new HintInterceptor());

E ti permetterà di modificare il CommandText . L'unico problema è che ora è allegato per ogni domanda del lettore che potrebbe essere un problema poiché alcuni di essi potrebbero essere influenzati negativamente da quel suggerimento. Immagino di poter fare qualcosa con il contesto per capire se il suggerimento è appropriato o meno, o peggio, potrei esaminare il CommandText stesso.

Non sembra proprio la soluzione più elegante o a grana fine.

Modifica :Dal interceptorContext , puoi ottenere i DbContexts , quindi ho definito un'interfaccia simile a questa:

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

E quindi ho creato una classe che deriva dal mio DbContext originale (generato da EF) e implementa l'interfaccia sopra. Poi ho cambiato il mio intercettore in questo modo:

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

Ora per usarlo, creo un contesto usando la mia classe derivata invece dell'originale, setto QueryHint a qualunque cosa io voglia che sia (recompile in questo caso) e impostare ApplyHint subito prima di eseguire il comando e successivamente ripristinarlo su false.

Per rendere tutto un po' più autonomo, ho finito per definire un'interfaccia come questa:

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

E ho esteso il mio contesto db in questo modo (potresti, ovviamente, usare solo una classe parziale per estendere anche la classe generata da EF):

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

E poi, per rendere la parte di accensione e spegnimento un po' più facile da gestire, ho definito questo:

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

Ora per usarlo, posso fare proprio questo:

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
}

Questo potrebbe essere leggermente eccessivo e potrebbe essere ulteriormente sviluppato (ad esempio, usando un enum per i suggerimenti disponibili invece di una stringa - o sottoclasse a recompile suggerimento per la query, quindi non è necessario specificare la stringa recompile ogni volta e rischio un errore di battitura), ma ha risolto il mio problema immediato.