2012年12月23日 星期日

觀察 ADO.NET Entity Framework 5.0 產生的 SQL Command 與取得 Entity 對應的 Table Name

這篇的文章標題蠻長的,其實這與以前的兩篇文章是有關連的,

觀察 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 了……

image

這是因為我們所處理的資料已經不再是 ObjectQuery<T> 而是 DbQuery<T>,所以無法使用 ToTraceString() 方法,就在我遍尋不著可以把 DbQuery<T> 轉換成 ObjectQuery<T> 之際,我所幸就直接把 DbQuery<T> 使用 ToString() 方法,看看會輸出什麼樣的內容時,居然給了我這樣的結果,

image

是的,直接吐出了 SQL Command 給我(操作的資料庫為 Oracle XE,所以顯示的 SQL Command 內容會與 MS SQL Server 的 T-SQL 有所不同),這倒底是怎麼回事呢?

於是我先前往 MSDN,看看有什麼資訊,不過得到的結果並沒有一個明確詳細的資料,

http://msdn.microsoft.com/zh-tw/library/system.data.entity.infrastructure.dbquery.tostring(v=vs.103).aspx

image

最後出動了 Telerik JustDecompile 來查看 EntityFramework 5.0 的原始碼內容,在找到 DbQuery 的原始碼後,看到 ToString() 方法內的程式是這樣寫的,

image

再追到 System.Data.Entity.Internal.Linq 的 InternalQuery,可以看到 ToString() 方法裡還是使用 ObjectQuery 的 ToTraceString() 方法,

image

這也說明了為何 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」,問題的內容與我所遇到的情況是一致的,而下方所標示的解答內容為,

image

不過我還是到 MSDN 看看 DbContext 類別的定義,

http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext(v=vs.103).aspx

image

DbContext 類別是有繼承實作 IObjectContextAdapter 介面,而 IObjectContextAdapter 介面裡只有一個 Property,這個 Property 類別就是 ObjectContext,

http://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.iobjectcontextadapter(v=vs.103).aspx

image

所以程式就做了以下的修改,如此一來就可以由 DbContext 轉換為 ObjectContext,再進而使用 ObjectContext 執行 CreateObjectSet<T>() 方法來取得 ObjectSet<T> 然後轉出 SQL Command 並取得 Table Name,

image

最後方法就修改為以下的內容:

#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?

嗯 …… 不過文章既然都寫了,還是發佈出去吧!就當做是一篇記錄,日後我忘記時還有個地方可以查看。

 

以上

沒有留言:

張貼留言

最近的留言