Recursively Mapping ExpandoObject

前端 未结 1 548
梦谈多话
梦谈多话 2020-12-18 03:34

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

相关标签:
1条回答
  • 2020-12-18 04:03

    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
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题