Inheriting base type mappings with AutoMapper

AutoMapper is a useful library that makes it easy to map properties between two types. You can declaratively define how you want properties mapped or just let it map based on simple conventions (like matching property names).  While working with this library I found out that if you have one set of mapping between base types and then another set of mapping between their respective child types then the child type mappings won’t inherit the base type mappings.

For example, given the follow types:

public class SourceBaseClass
{
    public string BaseProp1 { get; set; }
    public string BaseProp2 { get; set; }
}

public class SourceChildClass : SourceBaseClass
{
    public string ChildProp1 { get; set; }
    public string ChildProp2 { get; set; }
}

public class DestBaseClass
{
    public string BaseProp1 { get; set; }
    public string BasePropTwo { get; set; }
}

public class DestChildClass : DestBaseClass
{
    public string ChildProp1 { get; set; }
    public string ChildPropTwo { get; set; }
}

I created  one mapping  for the base types so that SourceBaseClass::BaseProp2 would map to DestBaseClass::BasePropTwo and created another mapping for the child types so that SourceChildClass::ChildProp2 would map to DestChildClass::ChildPropTwo.

Mapper.CreateMap<SourceBaseClass, DestBaseClass>()
    .ForMember(x => x.BasePropTwo, m => m.MapFrom(x => x.BaseProp2));

Mapper.CreateMap<SourceChildClass, DestChildClass>()
    .ForMember(x => x.ChildPropTwo, m => m.MapFrom(x => x.ChildProp2));

You may notice I don’t have a mapping for the other properties, but since the names of BaseProp1 and ChildProp1 are the same for both source and destination types the convention base mapping will take care of those.

With these mappings in place if you try to map  an instance of SourceChildClass to DestChildClass you will see that BasePropTwo does not get mapped.

[Fact]
public void Will_map_base_properties_on_child_types_when_child_types_used()
{
    var source = new SourceChildClass
    {
        ChildProp1 = "child1",
        ChildProp2 = "child2",
        BaseProp1 = "base1",
        BaseProp2 = "base2"
    };

    var res = Mapper.Map<SourceChildClass, DestChildClass>(source);

    Assert.Equal(source.ChildProp1, res.ChildProp1);
    Assert.Equal(source.ChildProp2, res.ChildPropTwo);
    Assert.Equal(source.BaseProp1, res.BaseProp1);   // Passes, because it matches convention
    Assert.Equal(source.BaseProp2, res.BasePropTwo); // Fails!, since it does not inherit this mapping from the BaseTypes mappings
}

A simple solution to this problem could be to duplicate the base type mapping in the child type mapping definitions.  But this is repetitious, especially if you have large types with many mappings on them. To avoid doing that I created an extension method called InheritMappingFromBaseType that will let you declare in a child type mapping that you want to inherit the mappings of your base type.

public static class MapperExtensions
{
    public static void InheritMappingFromBaseType<TSource, TDestination>(this IMappingExpression<TSource, TDestination> mappingExpression)
    {
        var sourceType = typeof(TSource);
        var desctinationType = typeof(TDestination);
        var sourceParentType = sourceType.BaseType;
        var destinationParentType = desctinationType.BaseType;

        mappingExpression
            .BeforeMap((x, y) => Mapper.Map(x, y, sourceParentType, destinationParentType))
            .ForAllMembers(x => x.Condition(r => NotAlreadyMapped(sourceParentType, destinationParentType, r)));
    }

    private static bool NotAlreadyMapped(Type sourceType, Type desitnationType, ResolutionContext r)
    {
        return !r.IsSourceValueNull &&
               Mapper.FindTypeMapFor(sourceType, desitnationType).GetPropertyMaps().Where(
                   m => m.DestinationProperty.Name.Equals(r.MemberName)).Select(y => !y.IsMapped()).All(b => b);
    }
}

Using the InheritMappingFromBaseType method changes the mapping slightly:

Mapper.CreateMap<SourceBaseClass, DestBaseClass>()
    .ForMember(x => x.BasePropTwo, m => m.MapFrom(x => x.BaseProp2));

Mapper.CreateMap<SourceChildClass, DestChildClass>()
    .ForMember(x => x.ChildPropTwo, m => m.MapFrom(x => x.ChildProp2))
    .InheritMappingFromBaseType();

The InheritMappingFromBaseType method will attempt to first map using the base types and then exclude those mapped properties from the child type mapping.  This method has saved me from duplicating a large set of mappings since I had several child types which all needed the same base type mappings.