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