递归映射ExpandoObject [英] Recursively Mapping ExpandoObject

查看:98
本文介绍了递归映射ExpandoObject的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用我有,以创建/删除的运行时性能使用ExpandoObject;不过,我有一个函数返回的ExpandoObject映射到相应的对象/类。所以我必须想出了一个小的映射,没有工作,但有三个问题:




  1. 它不会递归的内部对象映射ExpandoObject $像预想的那样b $湾

  2. 当我尝试映射INT为可空只是它会抛出一个类型
    不匹配,因为我无法找到一个方法来检测和。正常投它

  3. 字段不能映射公共字符串属性;



  4. 代码:



    I-执行:

     公共静态类映射< T>其中T:类
    {
    #区域属性

    私人静态只读字典<字符串的PropertyInfo> PropertyMap;

    #endregion

    #地区的构造函数

    静态映射(){PropertyMap = typeof运算(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic可| BindingFlags.Instance).ToDictionary(p值=> p.Name.ToLower()中,p =指p); }

    #endregion

    #地区的方法

    公共静态无效的地图(ExpandoObject源,T目的地)
    {
    如果(来源== NULL)
    抛出新的ArgumentNullException(源);
    如果(目标== NULL)
    抛出新的ArgumentNullException(目标);

    的foreach(源变种KV)
    {
    的PropertyInfo磷;
    如果(PropertyMap.TryGetValue(kv.Key.ToLower(),出P))
    {
    型propType = p.PropertyType;
    如果(kv.Value == NULL)如果
    {
    (propType.IsByRef&安培;!&安培;!propType.Name =Nullable`1)
    {
    抛出新的ArgumentException(不能为空);
    }
    }
    ,否则如果(kv.Value.GetType()= propType!)
    {
    抛出新的ArgumentException(类型不匹配);
    }
    p.SetValue(目的地,kv.Value,NULL);
    }
    }
    }

    #endregion
    }

    二:使用方法:

     公共静态无效的主要() 
    {
    类C =新的Class();
    动态O =新ExpandoObject();
    o.Name =卡尔;
    o.Level = 7;
    o.Inner =新将InnerClass
    {
    NAME =卡尔内,
    级= 10
    };

    映射器LT;类> .MAP(O,C);

    Console.Read();
    }

    内部类类
    {
    公共字符串名称{;组; }
    公众诠释?等级{搞定;组; }
    公共内蒙古将InnerClass {搞定;组; }
    公共字符串属性;
    }

    内部类将InnerClass
    {
    公共字符串名称{;组; }
    公众诠释?等级{搞定;组; }
    }


    解决方案

    3如果属性格式化这样公共字符串属性; 的获取属性没有得到它。




    哦,那不是一个性质,这是一个领域。如果你想考虑的领域也是如此。

     静态映射()
    {
    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可空只是它会抛出一个类型不匹配,因为我无法找到一种方法来检测并正确投放。




    为什么检查可空键入,让反射看着办吧。如果值是有效的,它会被分配。

     公共静态无效的地图(ExpandoObject源,T目的地)
    {
    如果(来源== NULL)
    抛出新的ArgumentNullException(源);
    如果(目标== NULL)
    抛出新的ArgumentNullException(目标);

    的foreach(源变种KV)
    {
    的PropertyInfo磷;
    如果(PropertyMap.TryGetValue(kv.Key.ToLower(),出P))
    {
    p.SetValue(目的地,kv.Value,NULL);
    }
    ,否则
    {
    字段信息F;
    如果(FieldMap.TryGetValue(kv.Key.ToLower(),从F))
    {
    f.SetValue(目的地,kv.Value);
    }
    }
    }
    }




    1 - 它不会递归ExpandoObject的内部对象映射像预想的那样




    似乎是有效的工作,你的将InnerClass 至少

      C类=新的Class()。 
    动态O =新ExpandoObject();
    o.Name =卡尔;
    o.Level = 7;
    o.Inner =新将InnerClass
    {
    NAME =卡尔内,
    级= 10
    };

    o.Property =我的财产价值; //不要忘记设置这个

    映射器LT;类> .MAP(O,C);



    修改:根据您的意见,我已经创建了两个重载方法 MergeProperty 。你可以写类似的重载方法领域。

     公共静态无效MergeProperty(的PropertyInfo PI,ExpandoObject源,对象目标)
    {
    型propType = pi.PropertyType;

    //不递归值类型,可空< T>和字符串
    如果(propType.IsValueType || propType == typeof运算(字符串))
    {
    VAR sourceVal = source.First(KVP => kvp.Key == pi.Name) 。值;
    如果(sourceVal!= NULL)
    pi.SetValue(目标,sourceVal,NULL);
    }
    ,否则//递归映射内部类属性
    {
    VAR道具= propType.GetProperties(BindingFlags.Public |
    BindingFlags.NonPublic可|
    的BindingFlags .Instance);

    的foreach(以道具变种P)
    {
    VAR sourcePropValue = source.First(KVP => kvp.Key == pi.Name).value的;
    VAR targetPropValue = pi.GetValue(目标,NULL);

    如果(sourcePropValue!= NULL)
    {
    如果(targetPropValue == NULL)//替换
    {
    pi.SetValue(目标,源。首先(KVP => kvp.Key == pi.Name).value的,NULL);
    }
    ,否则
    {
    MergeProperty(P,sourcePropValue,targetPropValue);
    }
    }
    }

    }
    }

    公共静态无效MergeProperty(的PropertyInfo PI,对象源,对象目标)
    {
    型propType = pi.PropertyType;
    的PropertyInfo sourcePi = source.GetType()的getProperty(pi.Name)。

    //不递归值类型,可空< T>和字符串
    如果(propType.IsValueType || propType == typeof运算(字符串))
    {
    VAR sourceVal = sourcePi.GetValue(源,NULL);
    如果(sourceVal!= NULL)
    pi.SetValue(目标,sourceVal,NULL);
    }
    ,否则//递归映射内部类属性
    {
    VAR道具= propType.GetProperties(BindingFlags.Public |
    BindingFlags.NonPublic可|
    的BindingFlags .Instance);

    的foreach(以道具变种P)
    {
    VAR sourcePropValue = sourcePi.GetValue(源,NULL);
    VAR targetPropValue = pi.GetValue(目标,NULL);

    如果(sourcePropValue!= NULL)
    {
    如果(targetPropValue == NULL)//替换
    {
    pi.SetValue(目标,sourcePi .GetValue(源,NULL),NULL);
    }
    ,否则
    {
    MergeProperty(P,sourcePropValue,targetPropValue);
    }
    }
    }

    }
    }

    您可以使用的方法是这样的:

     公共静态无效的地图(ExpandoObject源,T目的地)
    {
    如果(来源== NULL)
    抛出新的ArgumentNullException(源);
    如果(目标== NULL)
    抛出新的ArgumentNullException(目标);

    的foreach(源变种KV)
    {
    的PropertyInfo磷;
    如果(PropertyMap.TryGetValue(kv.Key.ToLower(),出P))
    {
    MergeProperty(P,来源,目的地);
    }
    ,否则
    {
    //做类似的字段合并
    }
    }
    }


    In my application i have to use ExpandoObject in order to create/delete properties during the runtime; However, i have to map the returned ExpandoObject of a function to the corresponding object/class. So i have came up with a small Mapper that does the job but with 3 problems:

    1. It does not recursively map the inner objects of the ExpandoObject as supposed.
    2. When i try to map int to a Nullable simply it will throw a type mismatch because i can't find a way to detect and cast it properly.
    3. Fields can't be mapped public string Property;.

    Code:

    I- Implementation:

    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: Usage:

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

    解决方案

    3- If the property is formated like this public string Property; the get properties does not get it.

    Oh, that's not a property, that's a field. If you want consider fields as well.

    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- When i try to map int to a Nullable simply it will throw a type mismatch because i can't find a way to detect and cast it properly.

    Why check for Nullable type, let reflection figure it out. If value is valid, it will be assigned.

    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 - It does not recursively map the inner objects of the ExpandoObject as supposed.

    Seems to work for your InnerClass at least.

    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);
    

    EDIT: based on your comments, I've create two overloaded methods MergeProperty. You can write similarly overloaded methods for fields.

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

    You can use the methods this way:

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

    这篇关于递归映射ExpandoObject的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆