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

  • 它不会递归地映射ExpandoObject的内部对象
    如预期的那样。
  • 当我尝试将int映射到Nullable时,它将抛出一个类型
    不匹配,因为我找不到正确检测和转换它的方法。
  • 字段无法映射为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
    }
    

    二:用法:
    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; }
    }
    

    最佳答案



    哦,那不是属性(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);
    }
    



    为什么要检查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);
                }
            }
        }
    }
    



    似乎至少可以为您的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
            }
        }
    }
    

    10-06 09:54
    查看更多