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.
再來介紹該如何使用此Library
1.請先至網站下載檔案,因為下載下來為SourceCode所以請先用VS編譯過.
2.編譯後尋找EFTracingProvider目錄底下的bin\debug有兩個dll,EFProviderWrapperToolkit.dll與EFTracingProvider.dll.
3.將這兩個dll複製到你想要的目錄.
4.開啟EF專案,使用加入參考方式加入此二個dll.
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>