2013年11月4日 星期一

初探 Entity Framework 6 - Intercepting Part.1

前面已經說明說 Entity Framework 6 所提供的兩個新功能,分別是「初探 Entity Framework 6 的 Async/Await 功能」「初探 Entity Framework 6 - Logging」,而這一篇將會介紹另一個功能「Intercepting」,Intercepting 就字意上的解釋就是「截取」,我們可以使用這個新功能將 EF 所執行的 SQL Command 給截取起來後再做處理(例如紀錄在文字檔內) 。

這一篇將會以「MSDN - Data Developer Center > Entity Framework > Logging and Intercepting Database Operations」的內容來操作以及說明。

 


在「MSDN - Data Developer Center > Entity Framework > Logging and Intercepting Database Operations」這篇教學文件裡針對 Intercepting 有提供一個範例程式「Example: Logging to NLog」,

image

不過在進行操作前要先看看「IDbCommandInterceptor」介面,

Interception interfaces

The interception code is built around the concept of interception interfaces. These interfaces inherit from IDbInterceptor and define methods that are called when EF performs some action. The intent is to have one interface per type of object being intercepted. For example, the IDbCommandInterceptor interface defines methods that are called before EF makes a call to ExecuteNonQuery, ExecuteScalar, ExecuteReader, and related methods. Likewise, the interface defines methods that are called when each of these operations completes. The DatabaseLogFormatter class that we looked at above implements this interface to log commands.

 

image

IDbCommandInterceptor 是繼承 IDbInterceptor 這個介面,主要是針對 EF 執行某些操作時做呼叫使用,而擷取的操作對象有:NonQueryExecuting, NonQueryExcuted, ReaderExecuting, ReaderExecuted, ScalarExecuting, ScalarExecuted。

NonQuery 為執行 Insert, Update, Delete 這些操作,

NonQueryExecuting,在執行 DbCommand.ExecuteNonQuery 前被呼叫使用。

NonQueryExecuted,在執行 DbCommand.ExecuteNonQuery 之後被呼叫使用。

Reader 為執行查詢(Query)操作,

ReaderExecuting,在執行 DbCommand.ExecuteReader 前被呼叫使用。

ReaderExecuted,在執行 DbCommand.ExecuteReader 之後被呼叫使用。

Scalar 為執行查詢操作,與 Reader 不同的是傳回單一值,例如傳回彙總值(count),

ScalarExecuting,在執行 DbCommand.ExecuteScalar 前被呼叫使用。

ScalarExecuted,在執行 DbCommand.ExecuteScalar 之後被呼叫使用。

 


我們來看看「MSDN - Data Developer Center > Entity Framework > Logging and Intercepting Database Operations」所提供的範例「NLogCommandInterceptor」,

image

public class NLogCommandInterceptor : IDbCommandInterceptor
{
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
 
    public void NonQueryExecuting(
        DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }
 
    public void NonQueryExecuted(
        DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }
 
    public void ReaderExecuting(
        DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }
 
    public void ReaderExecuted(
        DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }
 
    public void ScalarExecuting(
        DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        LogIfNonAsync(command, interceptionContext);
    }
 
    public void ScalarExecuted(
        DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        LogIfError(command, interceptionContext);
    }
 
    private void LogIfNonAsync<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
        if (!interceptionContext.IsAsync)
        {
            Logger.Warn("Non-async command used: {0}", command.CommandText);
        }
    }
 
    private void LogIfError<TResult>(
        DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
    {
        if (interceptionContext.Exception != null)
        {
            Logger.Error("Command {0} failed with exception {1}",
                command.CommandText, interceptionContext.Exception);
        }
    }
}

NLogCommandInterceptor 實做 IDbCommandInterceptor 的方法,並透過 NLog 將截取的資料給記錄下來,這邊所記錄的截取內容有兩種,一種是在 …Executing 去記錄下非「非同步」(沒有使用非同步處理)的執行內容,另一種是在 …Executed 去記錄當有發生 Exception 的執行內容。

這裡有用到 DbCommandInterceptionContext<TResult>這個類別,透過這個類別去判斷是否為非同步處理以及是否發生 Exception,並且取得 Exception。

教學文件提供了這個 NLogCommandInterceptor 類別,雖然在文章裡有稍微提到應該要怎麼設定以及使用方式,但我想應該很多人還是看得相當模糊,所以就在下面做一次操作示範。

 

Step.1

專案透過 NLog 安裝 NLog,

image

 

Step.2

在 NLog.config 裡加入 Warn, Error 的 target 以及 rule,

image

image

 

Step.3

在專案根目錄下建立一個「Interceptor」資料夾,然後在 Interceptor 資料夾內新增 NLogCommandInterceptor.cs 類別檔案,然後將教學文件裡的 NLogCommandInterceptor 程式內容給複製起來然後貼上建立的檔案裡,

image

image

 

Step.4

建立好 NLogCommandInterceptor 類別,也完成 NLog 的設定之後,接著就是要註冊使用 NLogCommandInterceptor,我們可以在 DbConfiguration 裡去做註冊使用,

image

或者是也可以在 Global.asax 的 Application_Start 方法內使用以下的方式做註冊使用,

image

 

執行結果

當我執行一個沒有使用非同步處理的頁面,例如:CustomerController 的 Index 方法,

image

因為是執行查詢處理,所以 NLogCommandInterceptor 裡的 ReaderExecuting 就會執行截取,

image

然後執行 LogIfNonAsync() 方法,

image

在類別為 DbCommand 的參數 command 裡可以取得 EF 所執行 SqlCommand 內容,

image

在 LogIfNonAsync 方法裡所執行的是使用 NLog 記錄 Warn 等級訊息內容,而我們在 NLog.config 裡對應 Warn 等級的 rule 是使用 WarnDebugger,將資料輸出到 Visual Studio 的 Output 視窗中,

image

所以最後就會在 Visual Studio 的 Output 視窗中看到 NLog 記錄的截取訊息內容,

image

好的,我們已經可以看到透過 NLogCommandInterceptor 的 ReaderExecuting 將 EF 所執行的 SQL Command 顯示於 Visual Studio 的 Output 視窗中,但不是還有設定另外一個嗎?就是當 EF 執行時發生 Exception 的時候……

因為實在是不想示範怎麼觸發 EF Exception 的發生,所以我這邊就決定改用另外一種方式來做,也就是在執行新增、刪除、修改(Insert, Delete, Update)的 SQL Command 用 NLog 給記錄在文字檔中,至於這些操作處理就在下一篇裡繼續說明。

 

以上

沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。