2013年2月1日 星期五

Entity Framework 5 - 取得 Entity 的 Property Names 與 KeyMembers

當專案使用 Entity Framework 4 的時候,使用我之前介紹過的方法都是可行的,例如:

取得 Entity Framework 中 Entity 的主鍵成員名稱(KeyMember)
取得 Entity Framework 中 Entity 對應 Table 的原生 Column Name

而當專案使用 Entity Framework 5 之後就會發現到這些方法全都不行了,既然都不能用了就想辦法解決,只是這次就真的像破頭了,像我都是使用 Database First 的方式來建立 Entity Model,所以當我一開始想要用之前的方法在加上反射的方式來試著找出我想要的資料時,發現到 EF5 所建立的 EDMX 已經是大大不同了,這篇文章就說明如何在使用 EF5 的情況下找出 Entity 的 Property Names 與 KeyMembers。

 


以下是專案使用 EF5 所建立的 Entity Model 內容,與以往使用 EF4 所建立的 Entity Model 有很大的不同,

image

在試過很多的方式想要取得 Entity Model 裡面的 Entity Property Names 與 KeyMembers 都不得其門而入,最後還是參拜 Google 大神來找答案,不過有關這方面的文章與資料都真的很少,想必像我這樣的需求是沒有多少人需要的,最後在 MSDN 論壇裡找到了一篇討論,其中就有人提供了解決的方式,

Get Primary Key Properties of a Entity

其中的解決方式節錄於下:

using System.Data.Metadata.Edm;
using System.Linq;
using System.Reflection;
...
 
var mw = new MetadataWorkspace(
    new [] { "res://*/" },
    new [] { Assembly.GetExecutingAssembly() });
var tables = mw.GetItems(DataSpace.CSpace);
foreach(var e in tables.OfType<EntityType>())
    System.Diagnostics.Trace.WriteLine(e.Name + ": " + string.Join("\n", e.KeyMembers));

其中有幾個類別是沒有接觸過的,所以就上 MSDN 查詢了類別說明,

MSDN - MetadataWorkspace 類別
表示 ADO.NET 中繼資料 (Metadata) 執行階段服務元件,可提供從各種來源擷取中繼資料的支援。

MetadataWorkspace.GetItem<T> 方法 (String, DataSpace)
使用指定的識別和資料模型傳回項目。

MSDN - DataSpace 列舉型別
為 Entity Framework 中的模型指定預設名稱。
DataSpace.CSpace - 表示概念模型的預設名稱。

 

 

取得 Entity Model 內的所有 Entity 名稱

使用了上面的方法後,先整理出一個 Method,這個 method 是用來取得 Entity Model 內的所有 Entity 名稱,

/// <summary>
/// Gets all entity table names.
/// </summary>
/// <returns></returns>
public static List<string> GetAllEntityNames()
{
    List<string> result = new List<string>();
 
    var mw = new MetadataWorkspace(
        new[] { "res://*/" },
        new[] { Assembly.GetExecutingAssembly() });
 
    var items = mw.GetItems(DataSpace.CSpace);
 
    result.AddRange(items.OfType<EntityType>().Select(x => x.Name));
 
    return result;
}

執行結果:

image

 

 

取得 Entity Model 內所有 Entity 的 KeyMembers

這一個 method 的內容就是直接取用參考資料,不過做了一點修改,就是返回的資料是用 Dictionary,Key 為 Entity Name,Value 為 Entity 的 KeyMembers,KeyMember 並不會只有一個,所以當有多個 KeyMember 時就與逗號間隔的方式把 KeyMember Name 組合起來,

/// <summary>
/// Gets all entity key members.
/// </summary>
/// <returns></returns>
public static Dictionary<string, string> GetAllEntityKeyMembers()
{
    Dictionary<string, string> result = new Dictionary<string, string>();
 
    var mw = new MetadataWorkspace(
        new[] { "res://*/" },
        new[] { Assembly.GetExecutingAssembly() });
 
    var tables = mw.GetItems(DataSpace.CSpace);
 
    foreach (var e in tables.OfType<EntityType>())
    {
        result.Add(e.Name, string.Join(",", e.KeyMembers));
    }
 
    return result;
}

執行結果:

image

 

 

取得單一 Entity 的 KeyMembers

上面的方法是取回 Entity Model 裡所有 Entity 的 KeyMembers,但實際上是比較少這樣做,而是會取個別 Entity 的 KeyMembers,所以就增加了取得單一 Entity KeyMembers 的方法,

/// <summary>
/// Keys the members.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static List<string> KeyMembers<T>() where T : class
{
    List<string> result = new List<string>();
 
    string entityName = typeof(T).Name;
 
    Dictionary<string, string> allEntitykeyMembers = EntityModelHelper.GetAllEntityKeyMembers();
 
    string keyMembers = allEntitykeyMembers.Where(x => x.Key == entityName).Select(x => x.Value).FirstOrDefault();
 
    if (!string.IsNullOrWhiteSpace(keyMembers))
    {
        result = keyMembers.Split(',').ToList();
    }
    return result;
}

執行結果:

image

image

 

 

取得 Entity 的所有 Property Name

上面的方法已經可以取得 Entity 的 KeyMembers,那也可以藉由 Member 屬性取得 Entity 的 Property Name,

/// <summary>
/// Entities the property names.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<string> EntityPropertyNames<T>()
{
    string entityName = typeof(T).Name;
 
    Dictionary<string, IEnumerable<string>> members = new Dictionary<string, IEnumerable<string>>();
 
    var mw = new MetadataWorkspace(
        new[] { "res://*/" },
        new[] { Assembly.GetExecutingAssembly() });
 
    var tables = mw.GetItems(DataSpace.CSpace);
 
    foreach (var e in tables.OfType<EntityType>())
    {
        members.Add
        (
            e.Name,
            e.Members.Select(m => m.Name)
        );
    }
 
    return members.Where(x => x.Key == entityName).Select(x => x.Value).FirstOrDefault();
}

執行結果:

image

但在上面的執行結果可以看到有出現了兩個屬性 Orders, Products,這兩個並不是 Order_Details 類別的原生屬性,而是經由關連而產生的 NavigationProperty,

MSDN - NavigationProperty 類別
表示在概念模型中從一個實體型別到另一個實體型別的巡覽。

在之前「取得 Entity Framework 中 Entity 對應 Table 的原生 Column Name」這篇文章也有說明過要如何取得 Entity 的原生屬性,可以就由 BuiltInTypeKind.PrimitiveType 來判斷是否為原生屬性,

修改後的 Method:

/// <summary>
/// Entities the property names.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<string> EntityPropertyNames<T>()
{
    string entityName = typeof(T).Name;
 
    Dictionary<string, IEnumerable<string>> members = new Dictionary<string, IEnumerable<string>>();
 
    var mw = new MetadataWorkspace(
        new[] { "res://*/" },
        new[] { Assembly.GetExecutingAssembly() });
 
    var tables = mw.GetItems(DataSpace.CSpace);
 
    foreach (var e in tables.OfType<EntityType>())
    {
        members.Add
        (
            e.Name,
            e.Members
                .Where(m => m.TypeUsage.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType)
                .Select(m => m.Name)
        );
    }
 
    return members.Where(x => x.Key == entityName).Select(x => x.Value).FirstOrDefault();
}

執行結果:

image

 

以上

沒有留言:

張貼留言

提醒

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