2014-09-25 275 views
3

我有一個設計,其中的類包含摘要對象的List <>,每個摘要是一個字典半動態屬性。也就是說,給定列表中的每個摘要在字典中都具有相同的鍵。我使用這種設計來構建一組動態的「屬性」來跟蹤彙總值,因爲根據我的項目規格,這些彙總值可以在運行時進行配置。如何將List <Dictionary <string,object >>轉換爲List <[具有動態屬性的新類]>

現在的問題是:如何平展此列表,以便列表中的每個項都被視爲 - 如果字典中的鍵是實際屬性?

我試過不同的變化將字典轉換爲列表,但這似乎天生錯誤,因爲我真的需要把鍵作爲屬性。我猜我需要使用C#4.0+和ExpandoObject的新動態特性,但我不太清楚。

下面的代碼顯示了基本設置,「flattenedSummary」給了我想要的 - 一個動態類型的新列表,其屬性是摘要字典的鍵。但是,這是有缺陷的,因爲我對屬性名稱進行了硬編碼,並且我無法這樣做,因爲直到運行時我纔會真正知道它們。

FlattenedSummary2版本試圖扁平化列表,但由於返回的類型仍然是列表而不是我想要的列表,所以變得不足。

public class Summary : Dictionary<string, object> 
    { 
    } 

    public class ClassA 
    { 
     public List<Summary> Summaries = new List<Summary>(); 
    } 

    static void Main(string[] args) 
    { 
     ClassA a = new ClassA(); 
     var summary = new Summary(); 
     summary.Add("Year", 2010); 
     summary.Add("Income", 1000m); 
     summary.Add("Expenses", 500m); 
     a.Summaries.Add(summary); 

     summary = new Summary(); 
     summary.Add("Year", 2011); 
     summary.Add("Income", 2000m); 
     summary.Add("Expenses", 700m); 
     a.Summaries.Add(summary); 

     summary = new Summary(); 
     summary.Add("Year", 2012); 
     summary.Add("Income", 1000m); 
     summary.Add("Expenses", 800m); 
     a.Summaries.Add(summary); 

     var flattenedSummary = from s in a.Summaries select new { Year = s["Year"], Income = s["Income"], Expenses = s["Expenses"] }; 
     ObjectDumper.Write(flattenedSummary, 1); 

     var flattenedSummary2 = Convert(a); 
     ObjectDumper.Write(flattenedSummary2, 1); 

     Console.ReadKey(); 
    } 

    public static List<ExpandoObject> Convert(ClassA a) 
    { 
     var list = new List<ExpandoObject>(); 
     foreach (Summary summary in a.Summaries) 
     { 
      IDictionary<string, object> fields = new ExpandoObject(); 
      foreach (var field in summary) 
      { 
       fields.Add(field.Key.ToString(), field.Value); 
      } 
      dynamic s = fields; 
      list.Add(s); 
     } 

     return list; 
    } 
+1

那麼如果你直到運行時纔會知道它們是什麼,那麼你如何設想在代碼中使用這些屬性?這僅僅是爲了數據綁定還是你從未真正直接使用這些屬性的東西? – jmcilhinney 2014-09-25 02:50:53

+0

使用此扁平化視圖的兩種方法:數據綁定到網格以進行調試,並從web api序列化爲json(或xml)。請參閱下面關於接受的答案的評論,以獲得json解決方案的細微變化。 – 2014-09-25 06:22:52

回答

0

丹尼爾,

我發現這篇文章有可能爲你工作的解決方案。 http://theburningmonk.com/2011/05/idictionarystring-object-to-expandoobject-extension-method/

所以,你會創建擴展方法...

public static ExpandoObject ToExpando(this IDictionary<string, object> dictionary) 
     { 
      var expando = new ExpandoObject(); 
      var expandoDic = (IDictionary<string, object>)expando; 

      // go through the items in the dictionary and copy over the key value pairs) 
      foreach (var kvp in dictionary) 
      { 
       // if the value can also be turned into an ExpandoObject, then do it! 
       if (kvp.Value is IDictionary<string, object>) 
       { 
        var expandoValue = ((IDictionary<string, object>)kvp.Value).ToExpando(); 
        expandoDic.Add(kvp.Key, expandoValue); 
       } 
       else if (kvp.Value is ICollection) 
       { 
        // iterate through the collection and convert any strin-object dictionaries 
        // along the way into expando objects 
        var itemList = new List<object>(); 
        foreach (var item in (ICollection)kvp.Value) 
        { 
         if (item is IDictionary<string, object>) 
         { 
          var expandoItem = ((IDictionary<string, object>)item).ToExpando(); 
          itemList.Add(expandoItem); 
         } 
         else 
         { 
          itemList.Add(item); 
         } 
        } 

        expandoDic.Add(kvp.Key, itemList); 
       } 
       else 
       { 
        expandoDic.Add(kvp); 
       } 
      } 

      return expando; 
     } 
從您的主要功能

則...

List<ExpandoObject> flattenSummary3 = new List<ExpandoObject>(); 
foreach (var s in a.Summaries) 
{ 
    flattenSummary3.Add(s.ToExpando()); 
} 

而且現在flattenSummary3變量將包含ExpandObjects的列表你可以通過屬性來引用。

我希望這會有所幫助。

+0

我必須做一些小的更改: (1)將ToExpando更改爲返回「dynamic」而不是ExpandoObject(2)在ToExpando方法中,將expando聲明爲動態而不是var。 (3)在調用代碼中,聲明flattenSummary3爲列表 這些更改允許將flattenSummary3視爲一個真正的動態列表(序列化爲json給出期望的屬性/值數組)。該解決方案仍然不允許數據綁定在WinForm DataGridView上工作 - 但這可能是一個單獨的問題。謝謝你的幫助,埃德! – 2014-09-25 06:29:29

0

雖然我接受埃德的答案,因爲它非常接近,我提供以下代碼以防其他人發現它有用。關鍵的變化是確保ExpandoObject的所有用途都被設置爲動態的,以便最終的列表是動態的。沒有這些更改,檢查列表中的類型仍然返回ExpandoObject(例如,json序列化,給了ExpandoObject而不是預期的屬性名稱/值)。

首先,ToExpando()方法(它或許應該被稱爲ToDynamic):

public static dynamic ToExpando(this IDictionary<string, object> dictionary) 
{ 
    dynamic expando = new ExpandoObject(); 
    var expandoDic = (IDictionary<string, object>)expando; 

    // go through the items in the dictionary and copy over the key value pairs) 
    foreach (var kvp in dictionary) 
    { 
     // if the value can also be turned into an ExpandoObject, then do it! 
     if (kvp.Value is IDictionary<string, object>) 
     { 
      var expandoValue = ((IDictionary<string, object>)kvp.Value).ToExpando(); 
      expandoDic.Add(kvp.Key, expandoValue); 
     } 
     else if (kvp.Value is ICollection) 
     { 
      // iterate through the collection and convert any strin-object dictionaries 
      // along the way into expando objects 
      var itemList = new List<object>(); 
      foreach (var item in (ICollection)kvp.Value) 
      { 
       if (item is IDictionary<string, object>) 
       { 
        var expandoItem = ((IDictionary<string, object>)item).ToExpando(); 
        itemList.Add(expandoItem); 
       } 
       else 
       { 
        itemList.Add(item); 
       } 
      } 

      expandoDic.Add(kvp.Key, itemList); 
     } 
     else 
     { 
      expandoDic.Add(kvp); 
     } 
    } 

    return expando; 
} 

而且調用代碼是這樣的:

List<dynamic> summaries = new List<dynamic>(); 
    foreach (var s in a.Summaries) 
    { 
     summaries.Add(s.DynamicFields.ToExpando()); 
    } 

還是更加緊湊版本:

a.Summaries.Select(s => s.DynamicFields.ToExpando()) 

以上所有內容都提供了一個可引用爲的對象:

int year = a.Summaries[0].Year; // Year is a dynamic property of type int 
    decimal income = a.Summaries[0].Income; // Income is a dynamic property of type decimal 

當然,這個想法是我不知道的性質 - 但它們可以被序列化到JSON或與一些調整,用來綁定爲了顯示網格或其他UI元素。

相關問題