C# 展平嵌套对象以将其特性映射到目标对象

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 {

我正在尝试使用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
{
    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双向映射)
  • 相同名称和类型的属性映射
  • 您在此介绍的确切嵌入式结构
然后,考虑到这个结构,自己设置一个特定的映射可能是有意义的。如果我需要使用一些明确的映射约定(而不是依赖AutoMapper之类的通用映射器)进行映射,那么我倾向于这样做。为此,我有一些构建块,我倾向于在不同的上下文中重用它们。我快速组合了一个映射器,它适用于您从这些构建块中描述的问题,如下所示:

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();