2013-10-22 79 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

嗯,我想说的是。该映射器的工作是更新目标对象字段/属性,我的意思是更新是如果映射器在源中找到空字段/属性,它将不会用空值替换目标对应的字段/属性,而只是保留它,否则它会更新它。其次,我在这里也缺乏的是我希望映射器看看字段/属性是否包含更多的内部属性,它对它做了相同的处理(这是我的意思是递归映射) –