Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping one source class to multiple derived classes with automapper

Suppose i have a source class:

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

And some destination classes:

public class DestinationBase {      //Several properties }  public class DestinationDerived1 : DestinationBase {      //Several properties }  public class DestinationDerived2 : DestinationBase {      //Several properties } 

Then I wish the derived destination classes to inherit the automapper configuration of the baseclass because I do not want to have to repeat it, is there any way to achieve this?

Mapper.CreateMap<Source, DestinationBase>()     .ForMember(...)     // Many more specific configurations that should not have to be repeated for the derived classes     .ForMember(...);  Mapper.CreateMap<Source, DestinationDerived1 >()     .ForMember(...); Mapper.CreateMap<Source, DestinationDerived2 >()     .ForMember(...); 

When I write it like this it does not use the base mappings at all, and include doesn't seem to help me.

Edit: This is what I get:

public class Source {     public string Test { get; set; }     public string Test2 { get; set; } }  public class DestinationBase {     public string Test3 { get; set; } }  public class DestinationDerived1 : DestinationBase {     public string Test4 { get; set; } }  public class DestinationDerived2 : DestinationBase {     public string Test5 { get; set; } } 

Mapper.CreateMap<Source, DestinationBase>()               .ForMember(d => d.Test3, e => e.MapFrom(s => s.Test))               .Include<Source, DestinationDerived1>()               .Include<Source, DestinationDerived2>();          Mapper.CreateMap<Source, DestinationDerived1>()               .ForMember(d => d.Test4, e => e.MapFrom(s => s.Test2));          Mapper.CreateMap<Source, DestinationDerived2>()               .ForMember(d => d.Test5, e => e.MapFrom(s => s.Test2)); 

AutoMapper.AutoMapperConfigurationException : Unmapped members were found. Review the types and members below.

Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type

Source -> DestinationDerived1 (Destination member list)

Test3

like image 339
Erik Nordenhök Avatar asked Feb 05 '13 10:02

Erik Nordenhök


People also ask

Does AutoMapper map both ways?

Yes, or you can call CreateMap<ModelClass, ViewModelClass>(). ReverseMap() .

When should I use AutoMapper?

AutoMapper is used whenever there are many data properties for objects, and we need to map them between the object of source class to the object of destination class, Along with the knowledge of data structure and algorithms, a developer is required to have excellent development skills as well.

How do I use AutoMapper to list a map?

How do I use AutoMapper? First, you need both a source and destination type to work with. The destination type's design can be influenced by the layer in which it lives, but AutoMapper works best as long as the names of the members match up to the source type's members.

How does AutoMapper work in C#?

The AutoMapper in C# is a mapper between two objects. That is AutoMapper is an object-object mapper. It maps the properties of two different objects by transforming the input object of one type to the output object of another type.


2 Answers

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)); 
like image 187
Sergey Berezovskiy Avatar answered Sep 20 '22 16:09

Sergey Berezovskiy


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); 
like image 23
Fidan Avatar answered Sep 18 '22 16:09

Fidan