Mapping one source class to multiple derived classes with automapper

后端 未结 3 1379
忘掉有多难
忘掉有多难 2021-02-03 22:46

Suppose i have a source class:

public class Source
{
    //Several properties that can be mapped to DerivedBase and its subclasses
}

And some d

相关标签:
3条回答
  • 2021-02-03 23:28

    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);
    
    0 讨论(0)
  • 2021-02-03 23:42

    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));
    
    0 讨论(0)
  • 2021-02-03 23:44

    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>();
    
    0 讨论(0)
提交回复
热议问题