Entity Framework - 追蹤SQL Command

Entity Framework - 追蹤SQL Command

在Entity Framework中要查看到底最終轉譯的SQL command是甚麼相當困難,因為EF本身並沒有提供像Linq2sql的Log屬性來達成此點,這可能是因為Linq2sql比較單純只對SQL Server,而EF在設計上是與資料庫無關.

目前Entity Framework只能透過ObjectQuery的ToTraceString()來輸出轉譯後的查詢SQL語法.但對於SaveChange這類新增修改沒有方式能夠輸出到底實際上的SQL command是甚麼.

以往要追蹤的方式大概就是從SQL Profiler這類資料庫附加的軟體於資料庫端追蹤輸出,但因為資料庫通常不太可能單單只有一隻程式使用,在追蹤上有點麻煩.

 

還好有人找出了解決之道並實作Library提供給大家使用.專案參考http://code.msdn.microsoft.com/EFProviderWrappers


他的做法是從EntityConnection著手,底下圖的左邊是原來的EF架構,而右邊則使此Library的做法,中間多了一層Wrapping Provider.

 

image_3

 

再來介紹該如何使用此Library

1.請先至網站下載檔案,因為下載下來為SourceCode所以請先用VS編譯過.

2.編譯後尋找EFTracingProvider目錄底下的bin\debug有兩個dll,EFProviderWrapperToolkit.dll與EFTracingProvider.dll.

3.將這兩個dll複製到你想要的目錄.

4.開啟EF專案,使用加入參考方式加入此二個dll.

image

5.再來就是如何掛載Wrapping Provider,預設情況用EDM tool desinger設計好模型後,在VS中會產生.edmx與.designer.cs檔案,而.designer.cs中會產生對應的ObjectContext class,
而ObjectContext class則是使用EDM tool desinger時所輸入的名稱,預設會是xxxEntities.

而掛載方式就是繼承此class修改其建構式,譬如底下的範例是使用EDM建立一個TestEntities的ObjectContext.
新增一個class檔案,命名為ExtendedTestEntities.cs修改如下,

    public partial class ExtendedTestEntities : TestEntities
    {
        private TextWriter logOutput;
 
        public ExtendedTestEntities()
            : this("name=TestEntities")
        {
        }
 
        public ExtendedTestEntities(string connectionString)
            : base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
                    connectionString,
                    "EFTracingProvider"
            ))
        {
        }
 
        #region Tracing Extensions
 
        private EFTracingConnection TracingConnection
        {
            get { return this.UnwrapConnection<EFTracingConnection>(); }
        }
 
        public event EventHandler<CommandExecutionEventArgs> CommandExecuting
        {
            add { this.TracingConnection.CommandExecuting += value; }
            remove { this.TracingConnection.CommandExecuting -= value; }
        }
 
        public event EventHandler<CommandExecutionEventArgs> CommandFinished
        {
            add { this.TracingConnection.CommandFinished += value; }
            remove { this.TracingConnection.CommandFinished -= value; }
        }
 
        public event EventHandler<CommandExecutionEventArgs> CommandFailed
        {
            add { this.TracingConnection.CommandFailed += value; }
            remove { this.TracingConnection.CommandFailed -= value; }
        }
 
        private void AppendToLog(object sender, CommandExecutionEventArgs e)
        {
            if (this.logOutput != null)
            {
                this.logOutput.WriteLine(e.ToTraceString().TrimEnd());
                this.logOutput.WriteLine();
            }
        }
 
        public TextWriter Log
        {
            get { return this.logOutput; }
            set
            {
                if ((this.logOutput != null) != (value != null))
                {
                    if (value == null)
                    {
                        CommandExecuting -= AppendToLog;
                    }
                    else
                    {
                        CommandExecuting += AppendToLog;
                    }
                }
 
                this.logOutput = value;
            }
        }
 
 
        #endregion
    }

以上程式碼若要套用於你的程式只要修改TestEntities相關描述即可,剩下的可以直接Copy.

 

最後在程式中使用方式如下

            EFTracingProviderConfiguration.RegisterProvider();
 
            using (ExtendedTestEntities context = new ExtendedTestEntities())
            {                
                context.CommandExecuting += (sender, e) =>
                {
                    string s = e.ToTraceString();
                };
 
                context.CommandFinished += (sender, e) =>
                {
                    string s = e.ToTraceString();
                };
 
                ...
            }

透過CommandExecuting與CommandFinished事件則可攔截到最後EF下達SQL命令開始執行與執行後,另外還有執行失敗的事件,而透過callback函式的參數之ToTraceString()即可取得完整的轉譯後之SQL command.

 

這邊要注意一點,第一行的EFTracingProviderConfiguration.RegisterProvider();是用來註冊這個Library的Provider,因為這個Library除了Tracer SQL外還提供其他的功能,譬如說 Cache機制,都是透過Wrapping Provider來達成,如要了解可以參考官方網站之說明文件.

而除了在程式中使用,也可透過.net config機制來註冊,在相關config中加入如下的描述即可

<system.data>
  <DbProviderFactories>
    <add name="EF Tracing Data Provider"
         invariant="EFTracingProvider"
         description="Tracing Provider Wrapper"
         type="EFTracingProvider.EFTracingProviderFactory, EFTracingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
    <add name="EF Generic Provider Wrapper"
         invariant="EFProviderWrapper"
         description="Generic Provider Wrapper"
         type="EFProviderWrapperToolkit.EFProviderWrapperFactory, EFProviderWrapperToolkit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=def642f226e0e59b" />
  </DbProviderFactories>
</system.data>