C# 递归映射ExpandoObject

C# 递归映射ExpandoObject,c#,mapping,C#,Mapping,在我的应用程序中,为了在运行时创建/删除属性,我必须使用ExpandoObject;但是,我必须将返回的函数ExpandoObject映射到相应的对象/类。因此,我提出了一个小型绘图程序,它可以完成这项工作,但有3个问题: 它不会递归地映射ExpandooObject的内部对象 应该是这样的 当我尝试将int映射到一个可为null的值时,它将抛出一个类型 不匹配,因为我找不到一种方法来正确地检测和投射它 无法映射字段公共字符串属性 代码: 一.执行: 公共静态类映射器,其中T:class { #

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

  • 它不会递归地映射ExpandooObject的内部对象 应该是这样的
  • 当我尝试将int映射到一个可为null的值时,它将抛出一个类型 不匹配,因为我找不到一种方法来正确地检测和投射它
  • 无法映射字段
    公共字符串属性
  • 代码:

    一.执行:

    公共静态类映射器,其中T:class
    {
    #区域属性
    私有静态只读字典属性映射;
    #端区
    #区域导体
    静态映射器(){PropertyMap=typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToDictionary(p=>p.Name.ToLower(),p=>p);}
    #端区
    #区域方法
    公共静态void映射(ExpandoObject源,T目标)
    {
    if(source==null)
    抛出新的ArgumentNullException(“源”);
    如果(目标==null)
    抛出新的ArgumentNullException(“目标”);
    foreach(电源中的var kv)
    {
    房地产信息;
    if(PropertyMap.TryGetValue(kv.Key.ToLower(),out p))
    {
    类型propType=p.PropertyType;
    如果(千伏值==零)
    {
    如果(!propType.IsByRef&&propType.Name!=“可为null的`1”)
    {
    抛出新ArgumentException(“不可为null”);
    }
    }
    else if(kv.Value.GetType()!=propType)
    {
    抛出新ArgumentException(“类型不匹配”);
    }
    p、 设置值(目的地,千伏值,空);
    }
    }
    }
    #端区
    }
    
    二:用途:

    publicstaticvoidmain()
    {
    c类=新类();
    动态o=新的ExpandooObject();
    o、 Name=“Carl”;
    o、 级别=7;
    o、 内部=新的内部类
    {
    Name=“内部卡尔”,
    级别=10
    };
    Mapper.Map(o,c);
    Console.Read();
    }
    内部类
    {
    公共字符串名称{get;set;}
    公共整数?级别{get;set;}
    公共内部类内部{get;set;}
    公共字符串属性;
    }
    内部类内部类
    {
    公共字符串名称{get;set;}
    公共整数?级别{get;set;}
    }
    
    3-如果属性的格式如下
    公共字符串属性获取属性无法获取它

    哦,那不是财产,那是田地。如果你还想考虑字段,

    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映射到一个可为null的值时,它将抛出一个类型不匹配,因为我找不到一种方法来正确地检测和转换它

    为什么要检查
    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-它不会像假设的那样递归映射ExpandooObject的内部对象

    似乎至少对您的
    内部类
    有效

    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);
    
    您可以通过以下方式使用这些方法:

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

    很好的详细答案,但是,它将与InnerClass一起工作,但它将替换它,而不是映射它。。。正如我想更新(映射)其他非内部属性的值一样[换句话说(忽略源中的空值,不要用空值替换现有值)]。还考虑安排答案1,2,3而不是3,2,1:DCan你精心编写<代码>它将取代它不映射它< /代码>?你想克隆吗?如何映射和不替换
    非内部值?我不明白。嗯,我想说的是。该映射器的工作是更新目标对象字段/属性,我对更新的意思是,如果映射器在源中发现空字段/属性,它不会将目标对应的字段/属性替换为空,而是保留它,否则它将更新它。第二,我在这里也缺少的是,我希望映射器查看字段/属性是否包含更多的内部属性,它对其执行相同的过程(这就是我所说的递归映射)我真的很感激你愿意帮助我,希望你对我有耐心,因为这个问题对我来说非常关键,我真的想结束它。我得到了你想要的。。需要一些时间。。如果我能找到对你有用的东西,我会回来的。
    
    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);
    
    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
            }
        }
    }