最近我不得不在我的应用程序中使用ExpandoObject,所以我想知道如何使用我的旧Mapper来映射动态ExpandoOnjects,因为它不会映射字段和Expando的属性。
代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public class Mapper
{
private static readonly Dictionary<KeyValuePair<Type, Type>, object> Maps = new Dictionary<KeyValuePair<Type, Type>, object>();
private static PropertyInfo[] _fromProperties;
private static PropertyInfo[] _toProperties;
private static FieldInfo[] _fromFields;
private static FieldInfo[] _toFields;
// Rules...
private static readonly Func<PropertyInfo, PropertyInfo, bool> MatchingProps = (t1, t2) => t1.Name == t2.Name && t1.PropertyType.Name == t2.PropertyType.Name;
private static readonly Func<FieldInfo, FieldInfo, bool> MatchingFields = (t1, t2) => t1.Name == t2.Name && t1.FieldType.Name == t2.FieldType.Name;
private static readonly Func<PropertyInfo, FieldInfo, bool> MatchingPropertyToField = (t1, t2) => t1.Name == t2.Name && t1.PropertyType.Name == t2.FieldType.Name;
private static readonly Func<FieldInfo, PropertyInfo, bool> MatchingFieldToProperty = (t1, t2) => t1.Name == t2.Name && t1.FieldType.Name == t2.PropertyType.Name;
public static void AddMap<TFrom, TTo>(Action<TFrom, TTo> map = null)
where TFrom : class
where TTo : class { Maps.Add(new KeyValuePair<Type, Type>(typeof(TFrom), typeof(TTo)), map); }
public static void Map<TFromType, TOType>(TFromType @from, TOType to)
{
var key = new KeyValuePair<Type, Type>(typeof(TFromType), typeof(TOType));
var map = (Action<TFromType, TOType>)Maps[key];
bool hasMapping = Maps.Any(x => x.Key.Equals(key));
if (!hasMapping)
throw new Exception(string.Format("No map defined for {0} => {1}", typeof(TFromType).Name, typeof(TOType).Name));
Type tFrom = typeof(TFromType);
Type tTo = typeof(TOType);
_fromProperties = tFrom.GetProperties();
_fromFields = tFrom.GetFields();
_toProperties = tTo.GetProperties();
_toFields = tTo.GetFields();
SyncProperties(@from, to);
SyncFields(@from, to);
if (!Equals(map, null))
map(@from, to);
}
private static void SyncProperties<TFromType, TOType>(TFromType objFrom, TOType objTo)
{
PropertyInfo[] fromProperties = _fromProperties;
PropertyInfo[] toProperties = _toProperties;
FieldInfo[] toFields = _toFields;
if (fromProperties != null && fromProperties.Any()) {
foreach (PropertyInfo fromProperty in fromProperties) {
if (toProperties.Any(x => x.Name == fromProperty.Name)) {
PropertyInfo destinationProperty = toProperties.FirstOrDefault(x => x.Name == fromProperty.Name);
if (MatchingProps(fromProperty, destinationProperty)) {
object val = fromProperty.GetValue(objFrom, null);
if (Equals(val, null)) continue;
if (!Equals(destinationProperty, null)) destinationProperty.SetValue(objTo, Convert.ChangeType(val, fromProperty.PropertyType), null);
}
}
if (toFields.Any(x => x.Name == fromProperty.Name)) {
FieldInfo destinationField = toFields.FirstOrDefault(x => x.Name == fromProperty.Name);
if (MatchingPropertyToField(fromProperty, destinationField)) {
object val = fromProperty.GetValue(objFrom, null);
if (Equals(val, null)) continue;
if (!Equals(destinationField, null)) destinationField.SetValue(objTo, val);
}
}
}
}
}
private static void SyncFields<TFromType, TOType>(TFromType objFrom, TOType objTo)
{
FieldInfo[] fromFields = _fromFields;
FieldInfo[] toFields = _toFields;
PropertyInfo[] toProperties = _toProperties;
if (fromFields != null && fromFields.Any()) {
foreach (FieldInfo fromField in fromFields) {
if (toFields.Any(x => x.Name == fromField.Name)) {
FieldInfo destinationField = toFields.FirstOrDefault(x => x.Name == fromField.Name);
if (MatchingFields(fromField, destinationField)) {
object val = fromField.GetValue(objFrom);
if (Equals(val, null)) continue;
if (!Equals(destinationField, null)) destinationField.SetValue(objTo, val);
}
}
if (toProperties.Any(x => x.Name == fromField.Name)) {
PropertyInfo destinationProperty = toProperties.FirstOrDefault(x => x.Name == fromField.Name);
if (MatchingFieldToProperty(fromField, destinationProperty)) {
object val = fromField.GetValue(objFrom);
if (Equals(val, null)) continue;
if (!Equals(destinationProperty, null)) destinationProperty.SetValue(objTo, val, null);
}
}
}
}
}
}
用法:
static void Main()
{
dynamic o = new ExpandoObject();
o.Name = "Pouce";
o.Age = 42;
o.Rank = new Rank
{
Name = Ranks.Major
};
o.Guid = new Guid();
Soldier soldier = new Soldier();
Mapper.AddMap<ExpandoObject, Soldier>();
Mapper.Map(o, soldier);
Console.ReadLine();
}
public class Soldier
{
public string Name { get; set; }
public int Age { get; set; }
public Rank Rank { get; set; }
public Guid Guid { get; set; }
}
public class Rank
{
public Ranks Name { get; set; }
}
public enum Ranks
{
Private,
Specialist,
Corporal,
Sergeant,
Captain,
Major,
Colonel,
General
}
<object,
object>
映射中完美运行;但是当提供<ExpandoObject, object>
时
它没有映射任何东西。答案 0 :(得分:1)
这是因为ExpandoObject
的属性不是真正的.NET属性。通过将ExpandoObject
与dynamic
关键字一起使用,您可以向对象提供任意属性,这在运行时由DLR
处理。
您不能在GetProperties()
的动态实例上使用常规静态类型的方法GetFields()
和ExpandoObject
。
要将Mapper
扩展为消费ExpandObject
,您必须将其视为一种特殊情况。
请参阅我的回答here它可能对您有帮助。
编辑:使用ExpandoObject
进行反思并不困难。但是,您不会从中获得一组PropertyInfo
或FieldInfo
。你得到KeyValuePair<string, object>
。因此,您可能需要添加一个此类KeyValuePair
的数组来存储信息。
在Map()
方法中,您可以将ExpandoObject
视为特例:
if (tFrom == typeof(ExpandoObject)) {
_fromExpandoProperties = @from.Select(kvp => kvp).ToArray();
// where _fromExpandoProperties is of type KeyValuePair<string, object>[]
} else {
_fromProperties = tFrom.GetProperties();
}
要获取该媒体资源的名称和价值,您可以使用.Key
和.Value
代替.PropertyType.Name
和.GetValue()
。
您必须在所有代码中考虑此专业化。