EntityFramework的多種記錄日誌方式,記錄錯誤並分析執行時間過長緣由(系列4)

前言

Entity Framework 延伸系列目錄html

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

相關文章
相關標籤/搜索