今天咱们来聊聊EF的日志记录.sql
一个好的数据库操做记录不单单能够帮你记录用户的操做,数据库
更应该能够帮助你得到效率低下的语句来帮你提升运行效率安全
废话很少说,咱们开始多线程
系统:WIN7ide
数据库:SQL Server2008性能
相关技术:MVC5 EF6.0+优化
1、修改配置文件ui
咱们先来看看最简化的EF日志记录,任何代码都不用改,在你的配置文件中加入以下配置便可自动记录:this
在你的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>
咱们到对应的地址下就能找相关的日志文件了以下:
2、简单封装:
编写一个本身的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进行讨论.欢迎.