分享一个 .NET 通过监听器拦截 EF 消息写日志的详细例子

前言

EF 开发效率确实很高也很便捷,但当它发生错误时,也挺让人头疼的,为什么?因为 EF 就像是一个黑盒子,一切全被封装起来,出错的时候很难定位原因,如果能够知道并打印 EF 生成的 SQL 语句,对于定位 EF 错误,就很有帮助。

程序员的才智是无限的,虽然 EF 有这个那个的问题,但程序员却总比这些问题多一个办法。

下面分享一个 .NET 通过监听器拦截 EF 消息写日志的详细例子。

Step By Step 步骤

  1. 创建自定义监听器类,拦截 EF 的命令执行事件(留意注释)

    c# 复制代码
    /// <summary>
    /// 监听并写 EF 生成的 SQL 到日志
    /// </summary>
    public class EFIntercepterLogging : IDbCommandInterceptor
    {
        /// <summary>
        /// 实现接口的 NonQueryExecuting 方法
        /// </summary>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        public void NonQueryExecuting(
            DbCommand command, 
            DbCommandInterceptionContext<int> interceptionContext)
        {
            LogWhenExecuting(command, interceptionContext);
        }
    
        /// <summary>
        /// 实现接口的 NonQueryExecuted 方法
        /// </summary>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        public void NonQueryExecuted(
            DbCommand command, 
            DbCommandInterceptionContext<int> interceptionContext)
        {
            LogIfError(command, interceptionContext);
        }
    
        /// <summary>
        /// 实现接口的 ReaderExecuting 方法
        /// </summary>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        public void ReaderExecuting(
            DbCommand command, 
            DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            LogWhenExecuting(command, interceptionContext);
        }
    
        /// <summary>
        /// 实现接口的 ReaderExecuted 方法
        /// </summary>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        public void ReaderExecuted(
            DbCommand command, 
            DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            LogIfError(command, interceptionContext);
        }
    
        /// <summary>
        /// 实现接口的 ScalarExecuting 方法
        /// </summary>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        public void ScalarExecuting(
            DbCommand command, 
            DbCommandInterceptionContext<object> interceptionContext)
        {
            LogWhenExecuting(command, interceptionContext);
        }
    
        /// <summary>
        /// 实现接口的 ScalarExecuted 方法
        /// </summary>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        public void ScalarExecuted(
            DbCommand command, 
            DbCommandInterceptionContext<object> interceptionContext)
        {
            LogIfError(command, interceptionContext);
        }
    
        /// <summary>
        /// 写 EF 执行中的 SQL 日志,Debug 级别,用在 Executing 方法
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        private void LogWhenExecuting<TResult>(
            DbCommand command, 
            DbCommandInterceptionContext<TResult> interceptionContext)
        {
            Logger.DebugFormat("Executing the following SQL: [{0}]", command.CommandText);
        }
    
        /// <summary>
        /// 出现异常时写日志到 Log4net
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="command"></param>
        /// <param name="interceptionContext"></param>
        private void LogIfError<TResult>(
            DbCommand command, 
            DbCommandInterceptionContext<TResult> interceptionContext)
        {
            if (interceptionContext.Exception != null)
            {
                var errMsg = new StringBuilder(16);
                errMsg.AppendLine("Error occurred when executing the following SQL: ");
                errMsg.AppendLine(command.CommandText);
                foreach (DbParameter param in command.Parameters)
                {
                    errMsg.AppendLine($"ParameterName:[{param.ParameterName}] -- DbType:[{param.DbType}] -- Value:[{param.Value}]");
                }
                Logger.Error(errMsg, interceptionContext.Exception);
            }
        }
    }
  2. 在 Global.asax 中的 Application_Start 方法注册监听器

    c# 复制代码
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        //autofac注册
        AutofacConfig.RegisterConfig();
        //注册 EF log 监听器
        DbInterception.Add(new EFIntercepterLogging());
    }
  3. 这样就实现了监听器拦截 EF 消息写日志的功能,当运行程序执行 EF 语句时,就会自动将 EF 生成的 SQL 写到日志文件中

总结

通过实现自定义监听器类并注册监听器,可以跟踪和记录 EF 的操作和事件,在开发过程中更好地了解和调试 EF 的行为,不失为排查 EF 问题和优化 EF 性能的一个好方法。

我是老杨,一个奋斗在一线的资深研发老鸟,让我们一起聊聊技术,聊聊程序人生,共同学习,

相关推荐
曹牧4 小时前
C#:未被引用的函数
c#
上位机妹子10 小时前
EF Core 两种继承映射:每层次一张表(TPH)与每类型一张表(TPT)
数据库·c#·.netcore·ef core
小羊没烦恼!11 小时前
jQuery1.5的改进细节
java·服务器·开发语言·前端·c#
下页、再停留12 小时前
【C#桌面客户端系列学习-8】批量导出数据为csv格式
开发语言·c#·visual studio
波力海苔夹心脆67513 小时前
C# 视觉检测实战:PLC 按钮触发 VisionPro 检测,OK 亮灯 / NG 灭灯(西门子 PLC,含界面显示与图片存档)
开发语言·经验分享·c#·自动化·视觉检测·.net
kim14 小时前
一个人 + AI,用 WinForms 搓了个 376KB 的 Windows 便签:便签 / 截图 / 标注 / OCR / 待办全塞进去了
c#
hez201015 小时前
一行代码在 .NET 项目里启用 Satori GC
c#·.net·.net core·gc·clr
宇卿.15 小时前
C#语句:
开发语言·c#
咕白m62515 小时前
使用 C# 将 TIFF 转换为 PDF
前端·c#
JQLvopkk16 小时前
HslCommunication,一个工具测遍所有 PLC
开发语言·人工智能·c#·交互