Mapping one source class to multiple derived classes with automapper

[亡魂溺海] 提交于 2019-12-02 22:21:18

Include derived mappings into base mapping:

Mapper.CreateMap<Source, DestinationBase>()
    .ForMember(d => d.Id, op => op.MapFrom(s => s.Id)) // you can remove this
    .Include<Source, DestinationDerived1>()
    .Include<Source, DestinationDerived2>();

Mapper.CreateMap<Source, DestinationDerived1>()
    .ForMember(d => d.Name, op => op.MapFrom(s => s.Text))
    .ForMember(d => d.Value2, op => op.MapFrom(s => s.Amount));

Mapper.CreateMap<Source, DestinationDerived2>()
    .ForMember(d => d.Value, op => op.MapFrom(s => s.Amount));

Usage:

Mapper.AssertConfigurationIsValid();
var s = new Source() { Id = 2, Amount = 10M, Text = "foo" };
var d1 = Mapper.Map<DestinationDerived1>(s);
var d2 = Mapper.Map<DestinationDerived2>(s);

See Mapping inheritance on AutoMapper wiki.


UPDATE: Here is full code of classes which works as it should.

public class Source
{
    public int Id { get; set; }
    public string Text { get; set; }
    public decimal Amount { get; set; }
}

public class DestinationBase
{
    public int Id { get; set; }
}

public class DestinationDerived1 : DestinationBase
{
    public string Name { get; set; }
    public decimal Value2 { get; set; }
}

public class DestinationDerived2 : DestinationBase
{
    public decimal Value { get; set; }
}

UPDATE (workaround of AutoMapper bug):

public static class Extensions
{
    public static IMappingExpression<Source, TDestination> MapBase<TDestination>(
        this IMappingExpression<Source, TDestination> mapping)
        where TDestination: DestinationBase
    {
        // all base class mappings goes here
        return mapping.ForMember(d => d.Test3, e => e.MapFrom(s => s.Test));
    }
}

And all mappings:

    Mapper.CreateMap<Source, DestinationBase>()
          .Include<Source, DestinationDerived1>()
          .Include<Source, DestinationDerived2>()
          .MapBase();

    Mapper.CreateMap<Source, DestinationDerived1>()
          .MapBase()
          .ForMember(d => d.Test4, e => e.MapFrom(s => s.Test2));

    Mapper.CreateMap<Source, DestinationDerived2>()
          .MapBase()
          .ForMember(d => d.Test5, e => e.MapFrom(s => s.Test2));

For Automapper 8.0.
Current version has new method IncludeAllDerived
Here's working example:

        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Source, DestinationBase>()
                .ForMember(dest => dest.Test3, opt => opt.MapFrom(src => src.Test))
                .IncludeAllDerived();

            cfg.CreateMap<Source, DestinationDerived1>()
                .ForMember(dest => dest.Test4, opt => opt.MapFrom(src => src.Test2));

            cfg.CreateMap<Source, DestinationDerived2>()
                  .ForMember(dest => dest.Test5, opt => opt.MapFrom(src => src.Test2));
        });

        var mapper = config.CreateMapper();

        var source = new Source { Test = "SourceTestProperty", Test2 = "SourceTest2Property" };
        var d1 = mapper.Map<DestinationDerived1>(source);
        var d2 = mapper.Map<DestinationDerived2>(source);

        Assert.Equal("SourceTestProperty", d1.Test3);
        Assert.Equal("SourceTest2Property", d1.Test4);

        Assert.Equal("SourceTestProperty", d2.Test3);
        Assert.Equal("SourceTest2Property", d2.Test5);

NB! For those who are having issues with derived interfaces. AutoMapper does not support registering against derived interfaces. Only classes are handled.

To make it work, you have to change your type reference for CreateMap to the class instead of interface.

Example:

interface Interface1 {}
class Class1: Interface1 {}
interface Interface2: Interface1 {}
class Class2: Class1, Interface2 {}

CreateMap<OtherClass, Interface1>().IncludeAllDerived();
CreateMap<OtherClass, Interface2>();

Any mapping against Interface2 will only use the first CreateMap. You will have to identify the second as

CreateMap<OtherClass, Class2>();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!