Database.Log 不记录来自实体框架中的拦截器的附加 SQL
Posted
技术标签:
【中文标题】Database.Log 不记录来自实体框架中的拦截器的附加 SQL【英文标题】:Database.Log not logging appended SQL from interceptor in Entity Framework 【发布时间】:2021-10-12 03:37:21 【问题描述】:我成功地将此代码附加到从实体框架创建的 SQL 查询中,并在 Scope 内使用拦截器,但随后调用的 Database.Log 在查询中没有显示 OPTION(RECOMPILE),所以我没有确保它甚至被执行。
调用代码
using (new OptionRecompileScope(_divisionPoolsRepository.DataContext))
divisionTeamResultsIds.AddRange(_divisionPoolsRepository.DataContext.DivisionBracketParticipants.Where(g => g.DivisionBracketParticipantPool.DivisionPoolId == divisionPoolId.Value).Select(g => g.DivisionGameTeamResultId).Distinct().ToList());
OptionRecompileScope.cs
private void AddOptionRecompile(IDbCommand command)
command.CommandText += " OPTION(RECOMPILE)";
以上代码来自此链接EF 6 Parameter Sniffing
DataContext.cs
public class DataContext : DbContext
private readonly ILogger _logger;
public DataContext(ILogger logger)
Database.SetInitializer<DataContext>(null);
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
_logger = logger;
if (Config.Debugging.LogDatabase)
Database.Log = q => _logger.Debug(q);
整个作用域类
public class OptionRecompileScope : IDisposable
private const string QueryHintText = " OPTION(RECOMPILE)";
private readonly OptionRecompileDbCommandInterceptor interceptor;
public OptionRecompileScope(DbContext context)
interceptor = new OptionRecompileDbCommandInterceptor(context);
DbInterception.Add(interceptor);
public void Dispose()
DbInterception.Remove(interceptor);
private class OptionRecompileDbCommandInterceptor : IDbCommandInterceptor
private readonly DbContext dbContext;
internal OptionRecompileDbCommandInterceptor(DbContext dbContext)
this.dbContext = dbContext;
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
if (ShouldIntercept(command, interceptionContext))
AddOptionRecompile(command);
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
if (ShouldIntercept(command, interceptionContext))
AddOptionRecompile(command);
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
private void AddOptionRecompile(IDbCommand command)
if (!command.CommandText.EndsWith(QueryHintText))
command.CommandText += QueryHintText;
private bool ShouldIntercept(IDbCommand command, DbCommandInterceptionContext interceptionContext)
return
command.CommandType == CommandType.Text &&
command is SqlCommand &&
interceptionContext.DbContexts.Any(interceptionDbContext => ReferenceEquals(interceptionDbContext, dbContext));
【问题讨论】:
你用DbInterception.Add(...)
注册拦截器吗?
是的,代码有,我说列出的代码被命中了。范围添加它。
好的,只是为了确定。这里的一个快速测试指出这是一个顺序问题。如果Log
在添加拦截器后 被订阅,它将记录修改后的命令,否则记录原始命令。毫无疑问,这与记录器本身也是一个拦截器有关,它们作为管道运行。
您可以寻求像this 这样的解决方案。 IE。只需在启动时注册一次拦截器并打开或关闭其效果。
但是在datacontext构造函数中不是在另一个之前添加了记录器,并且db是另一个拦截器的作用域密码吗?为什么这对日志记录很重要是一个 lambda,并且除非调用查询,否则它不会被执行。
【参考方案1】:
看起来顺序是问题所在。我刚刚在 OptionRecompileScope 构造函数中读取了它,现在输出是正确的。
public OptionRecompileScope(DbContext context, ILogger logger)
interceptor = new OptionRecompileDbCommandInterceptor(context);
DbInterception.Add(interceptor);
if (Config.Debugging.LogDatabase)
context.Database.Log = q => logger.Debug(q);
【讨论】:
以上是关于Database.Log 不记录来自实体框架中的拦截器的附加 SQL的主要内容,如果未能解决你的问题,请参考以下文章