tab parameter height 優化 系統 環境 build ado.net 所有

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)