EntityFramework的多种记录日志方式,记录错误并分析执行时间过长原因(系列4)

前言

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

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

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

废话不多说,我们开始

环境和相关技术

本文采用的环境与技术

系统:WIN7

数据库:SQL Server2008

相关技术:MVC5     EF6.0+

简单的记录

一、修改配置文件

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

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

<interceptors>
      <interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
        <parameters>
          <parameter value="D:\ttt\log.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进行讨论.欢迎.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏更流畅、简洁的软件开发方式

分页解决方案 —— GridView + QuickPager + QuickPager_SQL + DataAccessLibrary + 数据库

    这里要说的不仅仅是一个分页控件,而是一套解决方案,包括如何显示数据、显示分页导航,如何得到分页用的sql语句(等效于存储过程),如何提取数据,如何绑定控...

1948
来自专栏阿炬.NET

FSLIB.NETWORK 简易使用指南

3576
来自专栏不会写文章的程序员不是好厨师

日志那些事儿——谈谈需要日志输出的client jar应该如何设计

上篇文章提到了应该如何设计需要输出日志的client jar,大概有三个比较重要的点。

803
来自专栏西安-晁州

GraphQL介绍&使用nestjs构建GraphQL查询服务

GraphQL介绍&使用nestjs构建GraphQL查询服务(文章底部附demo地址) GraphQL一种用为你 API 而生的查询语言。出自于Faceboo...

3879
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(40)-精准在线人数统计实现-【过滤器+Cache】

上次的探讨没有任何结果,我浏览了大量的文章和个别系统的参考!决定用Cache来做,这可能有点难以接受但是配合mvc过滤器来做效果非常好! 由于之前的过滤器我们用...

1769
来自专栏菩提树下的杨过

puremvc框架之proxy

上一篇 puremvc框架之Command 里,已经学习了如何利用Command来解耦View层与业务逻辑的依赖,但是仍然有二个问题: 1、ButtonMedi...

2117
来自专栏智能大石头

在XCode中如何使用高级查询

对于一个框架来说,仅有基本的CURD不行,NewLife.XCode同时还提供了一个非常宽松的方式来使用高级查询,以满足各种复杂的查询需求。 (本文同样适用于其...

1706
来自专栏我就是马云飞

RxJava2 实战知识梳理(3) - 优化搜索联想功能

应用场景 几乎每个应用程序都提供了搜索功能,某些应用还提供了搜索联想。对于一个搜索联想功能,最基本的实现流程为:客户端通过EditText的addTextCha...

1947
来自专栏LinkedBear的个人空间

HttpClient入门及其应用 顶

这些需求可能或多或少的会发生在平时的开发中,针对每种情况,可能解决方案不止一种。本文将会使用HttpClient这种工具来讲解HttpClient的相关知识,以...

601
来自专栏算法+

谷歌开源项目Google Preview Image Extractor(PIEX) (附上完整demo代码)

前天偶然看到谷歌开源项目中有一个近乎无人问津的项目Google Preview Image Extractor(PIEX) 。 项目地址: https://gi...

3946

扫码关注云+社区