今天咱們來聊聊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進行討論.歡迎.