這篇的文章標題蠻長的,其實這與以前的兩篇文章是有關連的,
觀察 Entity Framework 轉換所產出的 SQL Command
動態取得 Entity Framework 中 Entity 對應的 TableName
這兩篇文章都是以使用 ADO.NET Entity Framework 4.0 以及 .NET 4.0 為背景,不過換成 ADO.NET Entity Framework 5.0 與 .NET 4.5 的使用情境下就會有所不同了,那兩篇文章內所說的方法就行不通,所以就必須要換另外的方式來完成需求。
觀察 ADO.NET Entity Framework 5.0 所產生的 SQL Command
在「觀察 Entity Framework 轉換所產出的 SQL Command」這篇文章當中有說到,我們要觀察 EF 所轉換產生的 SQL Command 時,可以對 ObjectQuert<T> 使用 ToTraceString() 來取得,但是當專案使用了 EF 5 以及 .NET 4.5 之後卻無法使用 ToTraceString() 來取得 SQL Command 了……
這是因為我們所處理的資料已經不再是 ObjectQuery<T> 而是 DbQuery<T>,所以無法使用 ToTraceString() 方法,就在我遍尋不著可以把 DbQuery<T> 轉換成 ObjectQuery<T> 之際,我所幸就直接把 DbQuery<T> 使用 ToString() 方法,看看會輸出什麼樣的內容時,居然給了我這樣的結果,
是的,直接吐出了 SQL Command 給我(操作的資料庫為 Oracle XE,所以顯示的 SQL Command 內容會與 MS SQL Server 的 T-SQL 有所不同),這倒底是怎麼回事呢?
於是我先前往 MSDN,看看有什麼資訊,不過得到的結果並沒有一個明確詳細的資料,
最後出動了 Telerik JustDecompile 來查看 EntityFramework 5.0 的原始碼內容,在找到 DbQuery 的原始碼後,看到 ToString() 方法內的程式是這樣寫的,
再追到 System.Data.Entity.Internal.Linq 的 InternalQuery,可以看到 ToString() 方法裡還是使用 ObjectQuery 的 ToTraceString() 方法,
這也說明了為何 DbQuery<T> 可以直接使用 ToString() 就能取得 SQL Command 的原因了。
取得 Entity 對應的 Table Name
在「動態取得 Entity Framework 中 Entity 對應的 TableName」這篇文章裡,我實作了一個方法用來取得 Entity 對應到資料庫的 Table Name,
public class ContextExtensions
{
#region -- GetTableName --
/// <summary>
/// Gets the name of the table.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="context">The context.</param>
/// <param name="dbType">Type of the db.</param>
/// <returns></returns>
public static string GetTableName<T>(ObjectContext context, DataBaseType dbType = DataBaseType.MS_SQL_SERVER) where T : class
{
string sql = context.CreateObjectSet<T>().ToTraceString();
string matchWords = string.Empty;
switch (dbType)
{
case DataBaseType.MS_SQL_SERVER:
matchWords = "FROM (?<table>.*) AS";
break;
case DataBaseType.Oracle:
matchWords = "FROM \"(?<schema>.*)\".\"(?<table>.*)\"\\s";
break;
}
Regex regex = new Regex(matchWords);
Match match = regex.Match(sql);
string table = match.Groups["table"].Value;
return table;
}
#endregion
public enum DataBaseType
{
MS_SQL_SERVER = 0,
Oracle = 1
}
同樣的在 EF 5.0 與 .NET 4.5 的情況下也出現了問題,問題就出現在 DbContext 並沒有 CreateObjectSet<T>() 方法,所以也就沒有 ToTraceString() 方法可以取得 SQL Command,後來在 StackOverflow 裡找到了這篇「How to convert DbSet in Entity framework to ObjectQuery」,問題的內容與我所遇到的情況是一致的,而下方所標示的解答內容為,
不過我還是到 MSDN 看看 DbContext 類別的定義,
http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext(v=vs.103).aspx
DbContext 類別是有繼承實作 IObjectContextAdapter 介面,而 IObjectContextAdapter 介面裡只有一個 Property,這個 Property 類別就是 ObjectContext,
所以程式就做了以下的修改,如此一來就可以由 DbContext 轉換為 ObjectContext,再進而使用 ObjectContext 執行 CreateObjectSet<T>() 方法來取得 ObjectSet<T> 然後轉出 SQL Command 並取得 Table Name,
最後方法就修改為以下的內容:
#region -- GetTableName --
/// <summary>
/// Gets the name of the table.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="context">The context.</param>
/// <param name="dbType">Type of the db.</param>
/// <returns></returns>
public static string GetTableName<T>(DbContext context, DataBaseType dbType = DataBaseType.Oracle) where T : class
{
ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
ObjectSet<T> objectSet = objectContext.CreateObjectSet<T>();
string sql = objectSet.ToTraceString();
string matchWords = string.Empty;
switch (dbType)
{
case DataBaseType.MS_SQL_SERVER:
matchWords = "FROM (?<table>.*) AS";
break;
case DataBaseType.Oracle:
matchWords = "FROM \"(?<schema>.*)\".\"(?<table>.*)\"\\s";
break;
}
Regex regex = new Regex(matchWords);
Match match = regex.Match(sql);
string table = match.Groups["table"].Value;
return table;
}
#endregion
話說這篇文章快寫完之前在 Google 搜尋相關資料時,赫然發現到原來中國那邊已經有人介紹過這兩個部分了,
LingzhiSun's Blog
Entity Framework 小技巧一 —— 如何从DbContext得到其内部封装的ObjectContext
Entity Framework 小技巧五 —— 如何得到EF查询生成的SQL?
嗯 …… 不過文章既然都寫了,還是發佈出去吧!就當做是一篇記錄,日後我忘記時還有個地方可以查看。
以上
good
回覆刪除請問
回覆刪除ToString()可以直接產生SQL command
為什麼不直接用string sql = context.Set.ToString();
然後直接Regex
這篇文章是五年多前所寫的,我巳經不太記得當時的情況
刪除