C# 展平嵌套对象以将其特性映射到目标对象
我正在尝试使用AutoMapper映射如下类:C# 展平嵌套对象以将其特性映射到目标对象,c#,automapper,hal,object-object-mapping,C#,Automapper,Hal,Object Object Mapping,我正在尝试使用AutoMapper映射如下类: class FooDTO { public int X { get; set; } public EmbeddedDTO Embedded { get; set; } public class EmbeddedDTO { public BarDTO Y { get; set; } public BazDTO Z { get; set; } } } class Foo {
class FooDTO
{
public int X { get; set; }
public EmbeddedDTO Embedded { get; set; }
public class EmbeddedDTO
{
public BarDTO Y { get; set; }
public BazDTO Z { get; set; }
}
}
class Foo
{
public int X { get; set; }
public Bar Y { get; set; }
public Baz Z { get; set; }
}
Mapper.CreateMap<FooDTO, Foo>()
.ForMember(f => f.Y, c => c.MapFrom(f => f.Embedded.Y))
.ForMember(f => f.Z, c => c.MapFrom(f => f.Embedded.Z));
Mapper.CreateMap<FooDTO, Foo>()
.AfterMap((source, dest) => Mapper.Map(source.Embedded, dest));
class HalResource
{
[JsonProperty("_links")]
public IDictionary<string, HalLink> Links { get; set; }
}
class HalResource<TEmbedded> : HalResource
{
[JsonProperty("_embedded")]
public TEmbedded Embedded { get; set; }
}
class HalLink
{
[JsonProperty("href")]
public string Href { get; set; }
}
对于这样的课程:
class FooDTO
{
public int X { get; set; }
public EmbeddedDTO Embedded { get; set; }
public class EmbeddedDTO
{
public BarDTO Y { get; set; }
public BazDTO Z { get; set; }
}
}
class Foo
{
public int X { get; set; }
public Bar Y { get; set; }
public Baz Z { get; set; }
}
Mapper.CreateMap<FooDTO, Foo>()
.ForMember(f => f.Y, c => c.MapFrom(f => f.Embedded.Y))
.ForMember(f => f.Z, c => c.MapFrom(f => f.Embedded.Z));
Mapper.CreateMap<FooDTO, Foo>()
.AfterMap((source, dest) => Mapper.Map(source.Embedded, dest));
class HalResource
{
[JsonProperty("_links")]
public IDictionary<string, HalLink> Links { get; set; }
}
class HalResource<TEmbedded> : HalResource
{
[JsonProperty("_embedded")]
public TEmbedded Embedded { get; set; }
}
class HalLink
{
[JsonProperty("href")]
public string Href { get; set; }
}
(FooDTO
是一种资源)
我知道我可以通过如下方式显式创建地图来实现:
class FooDTO
{
public int X { get; set; }
public EmbeddedDTO Embedded { get; set; }
public class EmbeddedDTO
{
public BarDTO Y { get; set; }
public BazDTO Z { get; set; }
}
}
class Foo
{
public int X { get; set; }
public Bar Y { get; set; }
public Baz Z { get; set; }
}
Mapper.CreateMap<FooDTO, Foo>()
.ForMember(f => f.Y, c => c.MapFrom(f => f.Embedded.Y))
.ForMember(f => f.Z, c => c.MapFrom(f => f.Embedded.Z));
Mapper.CreateMap<FooDTO, Foo>()
.AfterMap((source, dest) => Mapper.Map(source.Embedded, dest));
class HalResource
{
[JsonProperty("_links")]
public IDictionary<string, HalLink> Links { get; set; }
}
class HalResource<TEmbedded> : HalResource
{
[JsonProperty("_embedded")]
public TEmbedded Embedded { get; set; }
}
class HalLink
{
[JsonProperty("href")]
public string Href { get; set; }
}
是否有一种方法可以全局配置继承
HalResource
的所有类的映射,以便DTO的嵌入的属性直接映射到目标对象?
我尝试使用自定义IObjectMapper
,但事实证明,这比我预期的更具挑战性…如果您的用例如问题中所述那样有限,即:
- 从HalResource派生实例到直接POCO的单向映射(vs双向映射)
- 相同名称和类型的属性映射
- 您在此介绍的确切嵌入式结构
public class Mapper
{
private const BindingFlags DestConstructorFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags DestFlags = BindingFlags.Instance | BindingFlags.Public;
private const BindingFlags SrcFlags = BindingFlags.Instance | BindingFlags.Public;
private static readonly object[] NoArgs = new object[0];
private static readonly Type GenericEmbeddedSourceType = typeof(HalResource<>);
private readonly Dictionary<Type, Func<object, object>> _oneWayMap = new Dictionary<Type, Func<object, object>>();
public void CreateMap<TDestination, TSource>()
where TDestination : class
where TSource : HalResource
{
CreateMap(typeof(TDestination), typeof(TSource));
}
public void CreateMap(Type destType, Type srcType)
{
_oneWayMap[srcType] = InternalCreateMapper(destType, srcType);
}
public object Map<TSource>(TSource toMap) where TSource : HalResource
{
var mapper = default(Func<object, object>);
if (!_oneWayMap.TryGetValue(typeof(TSource), out mapper))
throw new KeyNotFoundException(string.Format("No mapping for {0} is defined.", typeof(TSource)));
return mapper(toMap);
}
public TDestination Map<TDestination, TSource>(TSource toMap)
where TDestination : class
where TSource : HalResource
{
var converted = Map(toMap);
if (converted != null && !typeof(TDestination).IsAssignableFrom(converted.GetType()))
throw new InvalidOperationException(string.Format("No mapping from type {0} to type {1} has been configured.", typeof(TSource), typeof(TDestination)));
return (TDestination)converted;
}
public void Clear()
{
_oneWayMap.Clear();
}
private static Func<object, object> InternalCreateMapper(Type destType, Type srcType)
{
// Destination specific constructor + setter map.
var destConstructor = BuildConstructor(destType.GetConstructor(DestConstructorFlags, null, Type.EmptyTypes, null));
var destSetters = destType
.GetProperties(DestFlags)
.Where(p => p.CanWrite)
.ToDictionary(k => k.Name, v => Tuple.Create(v.PropertyType, BuildSetter(v)));
// Source specific getter maps
var srcPrimPropGetters = CreateGetters(srcType);
var srcEmbeddedGetter = default(Func<object, object>);
var srcEmbeddedPropGetters = default(IDictionary<string, Tuple<Type, Func<object, object>>>);
var baseType = srcType.BaseType;
while (baseType != null && baseType != typeof(object))
{
if (baseType.IsGenericType && GenericEmbeddedSourceType.IsAssignableFrom(baseType.GetGenericTypeDefinition()))
{
var genericParamType = baseType.GetGenericArguments()[0];
if (srcPrimPropGetters.Any(g => g.Value.Item1.Equals(genericParamType)))
{
var entry = srcPrimPropGetters.First(g => g.Value.Item1.Equals(genericParamType));
srcPrimPropGetters.Remove(entry.Key);
srcEmbeddedGetter = entry.Value.Item2;
srcEmbeddedPropGetters = CreateGetters(entry.Value.Item1);
break;
}
}
baseType = baseType.BaseType;
}
// Build mapper delegate function.
return (src) =>
{
var result = destConstructor(NoArgs);
var srcEmbedded = srcEmbeddedGetter != null ? srcEmbeddedGetter(src) : null;
foreach (var setter in destSetters)
{
var getter = default(Tuple<Type, Func<object, object>>);
if (srcPrimPropGetters.TryGetValue(setter.Key, out getter) && setter.Value.Item1.IsAssignableFrom(getter.Item1))
setter.Value.Item2(result, getter.Item2(src));
else if (srcEmbeddedPropGetters.TryGetValue(setter.Key, out getter) && setter.Value.Item1.IsAssignableFrom(getter.Item1))
setter.Value.Item2(result, getter.Item2(srcEmbedded));
}
return result;
};
}
private static IDictionary<string, Tuple<Type, Func<object, object>>> CreateGetters(Type srcType)
{
return srcType
.GetProperties(SrcFlags)
.Where(p => p.CanRead)
.ToDictionary(k => k.Name, v => Tuple.Create(v.PropertyType, BuildGetter(v)));
}
private static Func<object[], object> BuildConstructor(ConstructorInfo constructorInfo)
{
var param = Expression.Parameter(typeof(object[]), "args");
var argsExp = constructorInfo.GetParameters()
.Select((p, i) => Expression.Convert(Expression.ArrayIndex(param, Expression.Constant(i)), p.ParameterType))
.ToArray();
return Expression.Lambda<Func<object[], object>>(Expression.New(constructorInfo, argsExp), param).Compile();
}
private static Func<object, object> BuildGetter(PropertyInfo propertyInfo)
{
var instance = Expression.Parameter(typeof(object), "instance");
var instanceCast = propertyInfo.DeclaringType.IsValueType
? Expression.Convert(instance, propertyInfo.DeclaringType)
: Expression.TypeAs(instance, propertyInfo.DeclaringType);
var propertyCast = Expression.TypeAs(Expression.Property(instanceCast, propertyInfo), typeof(object));
return Expression.Lambda<Func<object, object>>(propertyCast, instance).Compile();
}
private static Action<object, object> BuildSetter(PropertyInfo propertyInfo)
{
var setMethodInfo = propertyInfo.GetSetMethod(true);
var instance = Expression.Parameter(typeof(object), "instance");
var value = Expression.Parameter(typeof(object), "value");
var instanceCast = propertyInfo.DeclaringType.IsValueType
? Expression.Convert(instance, propertyInfo.DeclaringType)
: Expression.TypeAs(instance, propertyInfo.DeclaringType);
var call = Expression.Call(instanceCast, setMethodInfo, Expression.Convert(value, propertyInfo.PropertyType));
return Expression.Lambda<Action<object, object>>(call, instance, value).Compile();
}
}
公共类映射器
{
private const BindingFlags DestConstructorFlags=BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags DestFlags=BindingFlags.Instance | BindingFlags.Public;
private const BindingFlags SrcFlags=BindingFlags.Instance | BindingFlags.Public;
私有静态只读对象[]NoArgs=新对象[0];
私有静态只读类型GenericEmbeddedSourceType=typeof(HalResource);
专用只读词典_oneWayMap=新词典();
public void CreateMap()
目的地:班级
where TSource:HalResource
{
CreateMap(typeof(TDestination)、typeof(TSource));
}
公共void CreateMap(类型destType、类型srcType)
{
_oneWayMap[srcType]=InternalCreateMapper(destType,srcType);
}
公共对象映射(TSource-toMap),其中TSource:HalResource
{
变量映射器=默认值(Func);
if(!\u oneWayMap.TryGetValue(typeof(TSource),out mapper))
抛出新的KeyNotFoundException(string.Format(“没有定义{0}的映射。”,typeof(TSource));
返回映射器(toMap);
}
公共TDestination地图(TSource toMap)
目的地:班级
where TSource:HalResource
{
var转换=映射(toMap);
if(converted!=null&&!typeof(tdestinition).IsAssignableFrom(converted.GetType())
抛出新的InvalidOperationException(string.Format(“没有配置从类型{0}到类型{1}的映射。”,typeof(TSource),typeof(TDestination));
已转换的返回(TDestination);
}
公共空间清除()
{
_one-waymap.Clear();
}
私有静态函数InternalCreateMapper(类型destType,类型srcType)
{
//特定于目的地的构造函数+设置器映射。
var destConstructor=BuildConstructor(destType.GetConstructor(DestConstructorFlags,null,Type.EmptyTypes,null));
var destSetters=destType
.GetProperties(DestFlags)
.Where(p=>p.CanWrite)
.ToDictionary(k=>k.Name,v=>Tuple.Create(v.PropertyType,BuildSetter(v));
//源特定getter映射
var srcprimprogetters=CreateGetters(srcType);
var srcEmbeddedGetter=default(Func);
var srcEmbeddedPropGetter=默认值(IDictionary);
var baseType=srcType.baseType;
while(baseType!=null&&baseType!=typeof(对象))
{
if(baseType.IsGenericType&&GenericEmbeddedSourceType.IsAssignableFrom(baseType.GetGenericTypeDefinition()))
{
var genericParamType=baseType.getgenericalarguments()[0];
if(srcprimprogetters.Any(g=>g.Value.Item1.Equals(genericParamType)))
{
var entry=srcprimprogetters.First(g=>g.Value.Item1.Equals(genericParamType));
srcprimprogetters.Remove(entry.Key);
srcebeddedGetter=entry.Value.Item2;
srcebeddedprogetters=CreateGetters(entry.Value.Item1);
打破
}
}
baseType=baseType.baseType;
}
//构建映射器委托函数。
返回(src)=>
{
var结果=数据结构(NoArgs);
var srcebedded=srcebeddedGetter!=null?srcebeddedGetter(src):null;
foreach(destSetters中的var setter)
{
var getter=default(元组);
if(srcprimprogetters.TryGetValue(setter.Key,out getter)&&setter.Value.Item1.IsAssignableFrom(getter.Item1))
setter.Value.Item2(result,getter.Item2(src));
else if(srcebeddedPropGetters.TryGetValue(setter.Key,out getter)和&setter.Value.Item1.IsAssignableFrom(getter.Item1))
setter.Value.Item2(result,getter.Item2(srceembedded));
}
返回结果;
};
}
专用静态IDictionary CreateGetter(类型srcType)
{
返回srcType
.GetProperties(SrcFlags)
.其中(p=>p.CanRead)
.ToDictionary(k=>k.Name,v=>Tuple.Create(v.PropertyType,BuildGetter(v));
}
私有静态Func构建构造函数(ConstructorInfo ConstructorInfo)
{
var param=Expression.Parameter(typeof(object[]),“args”);
var argsExp=constructorInfo.GetParameters()
.Select((p,i)=>Expression.Convert(Expression.ArrayIndex(param,Expression.Constant(i)),p.ParameterType))
.ToArray();