Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Adicionando uma dica de consulta ao chamar a função com valor de tabela


me deparei com isso:

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

E parece que você pode fazer algo assim:
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 registre assim (eu fiz isso em Application_Start de global.asax.cs ):
DbInterception.Add(new HintInterceptor());

E permitirá que você altere o CommandText . O único problema é que agora está anexado para todos consulta do leitor, o que pode ser um problema, pois alguns deles podem ser afetados negativamente por essa dica. Acho que posso fazer algo com o contexto para descobrir se a dica é apropriada ou não, ou pior caso, poderia examinar o CommandText em si.

Não parece a solução mais elegante ou refinada.

Editar :Do interceptorContext , você pode obter os DbContexts , então eu defini uma interface que se parece com isso:
public interface IQueryHintContext
{
    string QueryHint { get; set; }
    bool ApplyHint { get; set; }
}

E então criei uma classe que deriva do meu DbContext original (gerado pelo EF) e implementa a interface acima. Então eu mudei meu interceptor para ficar assim:
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);
    }
}

Agora, para usá-lo, crio um contexto usando minha classe derivada em vez da original, defina QueryHint para o que eu quiser (recompile neste caso) e defina ApplyHint logo antes de executar o comando e configurá-lo de volta para false depois.

Para tornar tudo isso um pouco mais autocontido, acabei definindo uma interface assim:
public interface IQueryHintContext
{
    string QueryHint { get; set; }
    bool ApplyHint { get; set; }
}

E estendi meu contexto de banco de dados assim (você também pode usar uma classe parcial para estender a classe gerada pelo EF):
public class MyEntities_Ext : MyEntities, IQueryHintContext
{
    public string QueryHint { get; set; }
    public bool ApplyHint { get; set; }
}

E então, para tornar a parte de ligar e desligar um pouco mais fácil de manusear, defini isso:
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;
    }
}

Agora, para usá-lo, posso fazer apenas isso:
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
}

Isso pode ser um pouco exagerado e pode ser desenvolvido ainda mais (por exemplo, usando um enum para dicas disponíveis em vez de uma string - ou subclassificando um recompile dica de consulta para que você não precise especificar a string recompile toda vez e arriscar um erro de digitação), mas resolveu meu problema imediato.