AutoMapper -- inheritance mapping not working, same source, multiple destinations

kdawg picture kdawg · Dec 21, 2012 · Viewed 6.9k times · Source

Can I use inheritance mapping in AutoMapper (v2.2) for maps with the same Source type but different Destination types?

I have this basic situation (the real classes have many more properties):

public abstract class BaseViewModel
{
    public int CommonProperty { get; set;}
}

public class ViewModelA : BaseViewModel
{
    public int PropertyA { get; set; }
}

public class ViewModelB : BaseViewModel
{
    public int PropertyB { get; set; }
}

ViewModelA and ViewModelB are different representations of the same Entity class:

public class Entity
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
    public int Property3 { get; set; }
}

I want to reuse the same mapping for BaseViewModel for each ViewModel, such as:

Mapper.CreateMap<Entity, BaseViewModel>()
    .Include<Entity, ViewModelA>()
    .Include<Entity, ViewModelB>()
    .ForMember(x => x.CommonProperty, y => y.MapFrom(z => z.Property1));

Mapper.CreateMap<Entity, ViewModelA>()
    .ForMember(x => x.PropertyA, y => y.MapFrom(z => z.Property2));

Mapper.CreateMap<Entity, ViewModelB>()
    .ForMember(x => x.PropertyB, y => y.MapFrom(z => z.Property3));

But unfortunately, this doesn't seem to work. Calls like these:

var model = Mapper.Map<Entity, ViewModelA>(entity);

result in model having PropertyA mapped, but not CommonProperty. I believe I'm following the examples in https://github.com/AutoMapper/AutoMapper/wiki/Mapping-inheritance properly, but I'm afraid having multiple maps created with the same Source type is tripping AutoMapper up.

Any insights? I love the idea of grouping Base class mappings together, but this doesn't seem to work.

Answer

Jord&#227;o picture Jordão · Dec 21, 2012

Unfortunately in this case, AutoMapper seems to be registering only one child class mapping per source type, the last one (ViewModelB). This was probably designed to work with parallel hierarchies, not with a single source type.

To work around this, you can encapsulate the common mappings in an extension method:

public static IMappingExpression<Entity, TDestination> MapBaseViewModel<TDestination>(this IMappingExpression<Entity, TDestination> map)
  where TDestination : BaseViewModel { 
  return map.ForMember(x => x.CommonProperty, y => y.MapFrom(z => z.Property1));
}

And use it in the individual subclass mappings:

Mapper.CreateMap<Entity, ViewModelA>()
  .MapBaseViewModel<ViewModelA>()
  .ForMember(x => x.PropertyA, y => y.MapFrom(z => z.Property2));

Mapper.CreateMap<Entity, ViewModelB>()
  .MapBaseViewModel<ViewModelB>()
  .ForMember(x => x.PropertyB, y => y.MapFrom(z => z.Property3));