递归映射ExpandoObject

在我的应用程序中,我必须使用ExpandoObject才能在运行时创建/删除属性; 但是,我必须将函数的返回的ExpandoObject映射到相应的对象/类。 所以我想出了一个小型Mapper来完成这项工作,但有3个问题:

  1. 它不会递归地映射ExpandoObject的内部对象。
  2. 当我尝试将int映射到Nullable时,它会抛出一个类型不匹配,因为我找不到一种方法来检测并正确地转换它。
  3. 字段无法映射public string Property;

码:

I-实施:

 public static class Mapper where T : class { #region Properties private static readonly Dictionary 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 } 

二:用法:

 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.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; } } 

3-如果属性形成为此public string Property; get属性没有得到它。

哦,那不是财产,那是一个领域。 如果你想考虑字段。

 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时,它会抛出一个类型不匹配,因为我无法找到一种方法来检测并正确地转换它。

为什么检查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.Map(o, c); 

编辑 :基于您的评论,我创建了两个重载方法MergeProperty 。 您可以为字段编写类似的重载方法。

 public static void MergeProperty(PropertyInfo pi, ExpandoObject source, object target) { Type propType = pi.PropertyType; // dont recurse for value type, Nullable 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 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 } } }