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:\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進行討論.歡迎.
EntityFramework的多種記錄日誌方式,記錄錯誤並分析執行時間過長原因(系列4)