AutoMapper可以隐式地展平这个映射吗?

时间:2012-10-29 19:46:22

标签: automapper

我正在尝试在两个对象列表之间进行映射。源类型具有类型A的复杂属性;目标类型是类型A的展平子集以及源类型中的其他标量属性。

public class A
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Source
{
    public A MyA { get; set; }
    public int SomeOtherValue { get; set; }
}

public class Destination
{
    public string Name { get; set; }
    public int SomeOtherValue { get; set; }
}

如果不清楚,我希望Source.MyA.Name映射到Destination.NameSource.SomeOtherValue以映射到Destination.SomeOtherValue

实际上,类型A有十几个属性,其中80%映射到Destination中同名属性。如果我明确地在CreateMap中详细说明映射,我可以让事情发挥作用:

CreateMap<Source, Destination>()
    .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.MyA.Name));

这里的缺点是我希望避免为每个需要复制到ForMember的{​​{1}}属性添加A行。我希望我能做一些像:

Destination

但如果我尝试上述操作,则在注册映射时会出现运行时错误:“仅对类型上的顶级个人成员支持成员的自定义配置。”

由于

3 个答案:

答案 0 :(得分:5)

ADestination以及SourceDestination之间创建映射,然后使用AfterMap()在第二个中使用第一个映射

Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>()
      .AfterMap((s, d) => Mapper.Map<A, Destination>(s.MyA, d));

然后像这样使用它:

var res = Mapper.Map<Source, Destination>(new Source { SomeOtherValue = 7,  MyA = new A { Id = 1, Name = "SomeName" } });

答案 1 :(得分:0)

作为一种解决方法,您可以在目标类型中使用带有附加属性的自定义类型转换器,以避免递归。

[TestFixture]
public class MapComplexType
{
    [Test]
    public void Map()
    {
        Mapper.CreateMap<A, Destination>();

        Mapper.CreateMap<Source, Destination>().ConvertUsing(new TypeConvertor());
        var source = new Source
                         {
                             MyA = new A
                                       {
                                           Name = "Name"
                                       },
                                       SomeOtherValue = 5
                         };
        var dest = new Destination();
        Mapper.Map(source, dest);
        Assert.AreEqual(dest.Name, "Name");
    }
}

public class TypeConvertor : ITypeConverter<Source, Destination>
{
    public Destination Convert(ResolutionContext context)
    {
        var destination = (Destination) context.DestinationValue;
        if (!((Destination)context.DestinationValue).IsMapped || destination == null)
        {
            destination = destination ?? new Destination();
            destination.IsMapped = true; // To avoid recursion
            Mapper.Map((Source)context.SourceValue, destination);
                            destination.IsMapped = false; // If you want to map the same object few times
        }
        Mapper.Map(((Source)context.SourceValue).MyA, destination);
        return (Destination)context.DestinationValue;
    }
}

public class A
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Source
{
    public A MyA { get; set; }
    public int SomeOtherValue { get; set; }
}

public class Destination
{
    public string Name { get; set; }
    public int SomeOtherValue { get; set; }
    // Used only for mapping purposes
    internal bool IsMapped { get; set; }
}

答案 2 :(得分:0)

试试这个,

Mapper.CreateMap<A, Destination>();
Mapper.CreateMap<Source, Destination>()
    .ForMember(destination => destination.Name, options => options.MapFrom(source => Mapper.Map<A, Destination>(source.MyA).Name));

var objSource = new Source { SomeOtherValue = 7, MyA = new A { Id = 1, Name = "SomeName" } };
var result = Mapper.Map<Source, Destination>(objSource);