zoukankan      html  css  js  c++  java
  • EntityFramework的多种记录日志方式,记录错误并分析执行时间过长原因(系列4)

    Entity Framework 延伸系列目录

    今天我们来聊聊EF的日志记录.

    一个好的数据库操作记录不仅仅可以帮你记录用户的操作,

    更应该可以帮助你获得效率低下的语句来帮你提高运行效率

    废话不多说,我们开始

    环境和相关技术
    本文采用的环境与技术

    系统:WIN7

    数据库:SQL Server2008

    相关技术:MVC5     EF6.0+

    简单的记录

    一、修改配置文件

    我们先来看看最简化的EF日志记录,任何代码都不用改,在你的配置文件中加入如下配置即可自动记录:

    在你的EntityFramework节点下加入如下配置即可(这里需要注意的是第一个参数是你日志的输出地址):

    复制代码
    <interceptors>
          <interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
            <parameters>
              <parameter value="D:	ttlog.txt" />
              <parameter value="true" type="System.Boolean" />
            </parameters>
          </interceptor>
        </interceptors>
    复制代码

    我们到对应的地址下就能找相关的日志文件了如下:

    二、简单封装:

    编写一个自己的DBContext的基类如下:

    复制代码
     public class DataBaseContext<T> : DbContext where T:class,new()
    {
        //重写SaveChanges方法
         public override int SaveChanges()
         {
                  string sql = "";
                    //记录实体操作日志
                        this.Database.Log = (a) =>
                        {
    
                            sql += a;
                        };
                //这里的sql就是操作日志了,想记哪就记哪吧.这里我就不实现了.
                return base.SaveChanges();
          }
    }
    复制代码

     

    通过低级监听接口来进行监听

    如果你只是想单纯的记录,上面两种方式应该就能满足你了.

    我们记录的目的其实最重要的还是在于分析性能 下面就开始我们的重头戏.

    采用IDbCommandInterceptor接口进行EF的监听

    首先我们来看看这个接口里面到底有些什么:

    写过ADO.NET的人 应该对这些单词很熟悉了吧.(因为EF最终访问数据库的方式还是用的ADO.NET)

    注意:每个执行都有ed(执行完成后的监听)和ing(执行时的监听)

    下面我们来一步一步实现这个接口

    首先定义一个类(名字你随意):

    //名字可以随意,但是肯定要继承我们的监听接口 - - ,    
    public class DatabaseLogger : IDbCommandInterceptor
    {
    }

    然后我们继续,

    定义一个静态只读的ConcurrentDictionary作为我们的记录仓储,考虑到数据访问时多线程的情况很常见,所以我们采用线程安全的ConcurrentDictionary

    代码如下:

     public class DatabaseLogger : IDbCommandInterceptor
     {
    
            static readonly ConcurrentDictionary<DbCommand, DateTime> MStartTime = new ConcurrentDictionary<DbCommand, DateTime>();
    
     }

    接下来,我们来实现我们所需要的两个方法 一个为onStart来记录SQL语句执行开始的时间

    如下:

    //记录开始执行时的时间
     private static void OnStart(DbCommand command)
     {
                MStartTime.TryAdd(command, DateTime.Now);
     }

    然后实现我们的log方法来记录相关的SQL语句和错误信息

    复制代码
            private static void Log<T>(DbCommand command, DbCommandInterceptionContext<T> interceptionContext)
            {
    
                DateTime startTime;
                TimeSpan duration;
               //得到此command的开始时间
                MStartTime.TryRemove(command, out startTime);
                if (startTime != default(DateTime))
                {
                    duration = DateTime.Now - startTime;
                }
                else
                    duration = TimeSpan.Zero;
    
                var parameters = new StringBuilder();
                //循环获取执行语句的参数值
                foreach (DbParameter param in command.Parameters)
                {
                    parameters.AppendLine(param.ParameterName + " " + param.DbType + " = " + param.Value);
                }
    
               //判断语句是否执行时间超过1秒或是否有错
                if (duration.TotalSeconds > 1 || interceptionContext.Exception!=null)
                {
                        //这里编写记录执行超长时间SQL语句和错误信息的代码
                }
                else
                {
                        //这里编写你自己记录普通SQL语句的代码
                }
    
    
            }
    复制代码

    既然我们已经得到了想要的东西,那具体的记录方式,各位仁者见仁 智者见智 就随意了,所以我这就不写了.

    然后接着,我们要实现这个接口的6个方法,如下:

    复制代码
            public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                Log(command, interceptionContext);
            }
    
            public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                OnStart(command);
            }
    
            public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
    
                Log(command, interceptionContext);
            }
    
            public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                OnStart(command);
            }
            public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                Log(command, interceptionContext);
            }
    
            public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
    
                OnStart(command);
            }
    复制代码

    其实很简单,就是所有的ing执行我们之前写的OnStart方法,所有的ed执行我们的log方法即可.

    接下来,我们需要注入这个接口:

    这里我的Demo用的MVC所以我就在 Application_Start()中直接注入了,如下:

    protected void Application_Start()
    {
              //注入自己写的监听
                DbInterception.Add(new MiniProfiler_EFModel.DatabaseLogger());
    }

    这样我们就完成了整个监听的过程了~

    实现效果如下:

    我们得到了执行的秒数

    得到了执行的SQL语句:

    得到了SQL语句所对应的参数:

    大功告成!

    写在最后

    这里我只是帮各位通过监听来获取到相关的信息,具体如何优化,应该用什么东西进行记录,我就不过多的赘述,这是属于仁者见仁智者见智的东西,不过有兴趣的可以通过博客加我QQ进行讨论.欢迎.

    作者:顾振印 出处:http://www.cnblogs.com/GuZhenYin/ 如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面
  • 相关阅读:
    ExtJS Form布局
    UML系列 (六)如何提取用例技术?
    牛腩新闻发布系统 (4)验证码的生成
    DIV +CSS 系列详细教程 (一)初识
    Java、JavaScript、asp.net 、jquery 详细分析
    设计模式详细系列教程 (三)以网上购物通用的订单流程 详解状态模式
    设计模式详细系列教程 (四) 无处不在的单例模式
    SCM软件配置管理 (二) SVN管理平台搭建以及详细设置
    JAVA JDK环境变量的配置
    牛腩新闻发布系统 (5) 总结
  • 原文地址:https://www.cnblogs.com/shiningrise/p/5585149.html
Copyright © 2011-2022 走看看