2013-10-22 121 views
6

在我的應用程序中,我必須使用ExpandoObject以在運行時期間創建/刪除屬性;但是,我必須將函數的返回ExpandoObject映射到相應的對象/類。遞歸映射ExpandoObject

  1. 它不會遞歸ExpandoObject 內的物體像預想的那樣映射:所以我有一個小的映射,沒有工作,但有3個問題上來。
  2. 當我嘗試將int映射到Nullable時,它會拋出一個類型 不匹配,因爲我無法找到檢測並正確投射它的方法。
  3. 無法映射字段public string Property;

代碼:

I-實現:

public static class Mapper<T> where T : class 
{ 
    #region Properties 

    private static readonly Dictionary<string, PropertyInfo> PropertyMap; 

    #endregion 

    #region Ctor 

    static Mapper() { PropertyMap = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToDictionary(p => p.Name.ToLower(), p => p); } 

    #endregion 

    #region Methods 

    public static void Map(ExpandoObject source, T destination) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 
     if (destination == null) 
      throw new ArgumentNullException("destination"); 

     foreach (var kv in source) 
     { 
      PropertyInfo p; 
      if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) 
      { 
       Type propType = p.PropertyType; 
       if (kv.Value == null) 
       { 
        if (!propType.IsByRef && propType.Name != "Nullable`1") 
        { 
         throw new ArgumentException("not nullable"); 
        } 
       } 
       else if (kv.Value.GetType() != propType) 
       { 
        throw new ArgumentException("type mismatch"); 
       } 
       p.SetValue(destination, kv.Value, null); 
      } 
     } 
    } 

    #endregion 
} 

II:用法:

public static void Main() 
{ 
    Class c = new Class(); 
    dynamic o = new ExpandoObject(); 
    o.Name = "Carl"; 
    o.Level = 7; 
    o.Inner = new InnerClass 
       { 
         Name = "Inner Carl", 
         Level = 10 
       }; 

    Mapper<Class>.Map(o, c); 

    Console.Read(); 
} 

internal class Class 
{ 
    public string Name { get; set; } 
    public int? Level { get; set; } 
    public InnerClass Inner { get; set; } 
    public string Property; 
} 

internal class InnerClass 
{ 
    public string Name { get; set; } 
    public int? Level { get; set; } 
} 
+0

任何答案... –

回答

4

3-如果財產如此形成,public string Property;獲取屬性不會得到它。

哦,這不是一個屬性,這是一個領域。如果你想考慮領域。

static Mapper() 
{ 
    PropertyMap = typeof(T).GetProperties(BindingFlags.Public | 
               BindingFlags.NonPublic | 
               BindingFlags.Instance) 
               .ToDictionary(p => p.Name.ToLower(), p => p); 

    FieldMap = typeof(T).GetFields(BindingFlags.Public | 
               BindingFlags.NonPublic | 
               BindingFlags.Instance) 
               .ToDictionary(f => f.Name.ToLower(), f => f); 
} 

2 - 當我嘗試映射INT爲可空只是它會拋出一個類型不匹配,因爲我無法找到一種方法來檢測並正確投放。

爲什麼要檢查Nullable類型,讓反射算出來。如果值有效,它將被分配。

public static void Map(ExpandoObject source, T destination) 
{ 
    if (source == null) 
     throw new ArgumentNullException("source"); 
    if (destination == null) 
     throw new ArgumentNullException("destination"); 

    foreach (var kv in source) 
    { 
     PropertyInfo p; 
     if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) 
     { 
      p.SetValue(destination, kv.Value, null); 
     } 
     else 
     { 
      FieldInfo f; 
      if (FieldMap.TryGetValue(kv.Key.ToLower(), out f)) 
      { 
       f.SetValue(destination, kv.Value); 
      } 
     } 
    } 
} 

1 - 它不遞歸像預想的那樣的ExpandoObject的內對象映射。

似乎至少適用於您的InnerClass

Class c = new Class(); 
dynamic o = new ExpandoObject(); 
o.Name = "Carl"; 
o.Level = 7; 
o.Inner = new InnerClass 
{ 
    Name = "Inner Carl", 
    Level = 10 
}; 

o.Property = "my Property value"; // dont forget to set this 

Mapper<Class>.Map(o, c); 

編輯:根據您的意見,我已經創建了兩個重載方法MergeProperty。您可以爲字段編寫類似的重載方法。

public static void MergeProperty(PropertyInfo pi, ExpandoObject source, object target) 
{ 
    Type propType = pi.PropertyType; 

    // dont recurse for value type, Nullable<T> and strings 
    if (propType.IsValueType || propType == typeof(string)) 
    { 
     var sourceVal = source.First(kvp => kvp.Key == pi.Name).Value; 
     if(sourceVal != null) 
      pi.SetValue(target, sourceVal, null); 
    } 
    else // recursively map inner class properties 
    { 
     var props = propType.GetProperties(BindingFlags.Public | 
                BindingFlags.NonPublic | 
                BindingFlags.Instance); 

     foreach (var p in props) 
     { 
      var sourcePropValue = source.First(kvp => kvp.Key == pi.Name).Value; 
      var targetPropValue = pi.GetValue(target, null); 

      if (sourcePropValue != null) 
      { 
       if (targetPropValue == null) // replace 
       { 
        pi.SetValue(target, source.First(kvp => kvp.Key == pi.Name).Value, null); 
       } 
       else 
       { 
        MergeProperty(p, sourcePropValue, targetPropValue); 
       } 
      } 
     } 

    } 
} 

public static void MergeProperty(PropertyInfo pi, object source, object target) 
{ 
    Type propType = pi.PropertyType; 
    PropertyInfo sourcePi = source.GetType().GetProperty(pi.Name); 

    // dont recurse for value type, Nullable<T> and strings 
    if (propType.IsValueType || propType == typeof(string)) 
    { 
     var sourceVal = sourcePi.GetValue(source, null); 
     if(sourceVal != null) 
      pi.SetValue(target, sourceVal, null); 
    } 
    else // recursively map inner class properties 
    { 
     var props = propType.GetProperties(BindingFlags.Public | 
                BindingFlags.NonPublic | 
                BindingFlags.Instance); 

     foreach (var p in props) 
     { 
      var sourcePropValue = sourcePi.GetValue(source, null); 
      var targetPropValue = pi.GetValue(target, null); 

      if (sourcePropValue != null) 
      { 
       if (targetPropValue == null) // replace 
       { 
        pi.SetValue(target, sourcePi.GetValue(source, null), null); 
       } 
       else 
       { 
        MergeProperty(p, sourcePropValue, targetPropValue); 
       } 
      } 
     } 

    } 
} 

您可以使用的方法是這樣的:?

public static void Map(ExpandoObject source, T destination) 
{ 
    if (source == null) 
     throw new ArgumentNullException("source"); 
    if (destination == null) 
     throw new ArgumentNullException("destination"); 

    foreach (var kv in source) 
    { 
     PropertyInfo p; 
     if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) 
     { 
      MergeProperty(p, source, destination); 
     } 
     else 
     { 
      // do similar merge for fields 
     } 
    } 
} 
+0

那麼好的詳細的答案,但是,它會隨着工作將InnerClass但它會取代它不映射。 ..因爲我想更新(映射)值,因爲它已與其他非內部屬性[換句話說(忽略來自源的空值,並且不用現有的替換爲空值)]完成。還可以考慮安排答案1,2,3而不是3,2,1:D –

+0

您能否詳細說明它會替換它而不是映射它?你想克隆? 「非內在價值」如何映射和不被替換?我不明白。 – YK1

+0

嗯,我想說的是。該映射器的工作是更新目標對象字段/屬性,我的意思是更新是如果映射器在源中找到空字段/屬性,它將不會用空值替換目標對應的字段/屬性,而只是保留它,否則它會更新它。其次,我在這裏也缺乏的是我希望映射器看看字段/屬性是否包含更多的內部屬性,它對它做了相同的處理(這是我的意思是遞歸映射) –