X

Using AutoMapper to build base class for mappers between domain classes and models

It is often good idea to isolate our domain model from consuming applications by using service layer and data transfer objects (DTO) or application specific models. Using DTO-s means that we need two-way mapping between domain classes and DTO-s. In this posting I will show you how to use AutoMapper to build generic base class for your mappers.

AutoMapper

AutoMapper is powerful object to object mapper that is able to do also smart and complex mappings between objects. Also you can modify existing and define your own mappings. Although it is possible to write way faster lightweight mappers still AutoMapper offers very good performance considering all the nice features it provides.

What’s most important – AutoMapper is easy to use and it fits perfect to the context of this posting.

Why mapping?

To those who have no idea about the problem scope I explain a little bit why mapping between domain classes and application specific models or DTO-s is needed. Often domain classes have complex dependencies between each other and they may also have complex dependencies with their technical environment. Domain classes may have cyclical references that makes it very hard to serialize them to text based formats. And domain classes may be hard to create.

By example, if you are using vendor offered powerful grid component then this component may want to serialize its data source so it can use it on client side to provide quick sorting and filtering of grid data. Moving from server to client means serialization to JSON or XML. If our domain objects have cyclical references (and it is normal they have) then we are in trouble. We have to use something lighter and less powerful, so we use DTO-s and models.

If you go through all complexities mentioned before you will find more issues with using domain classes as models. As we have to use lightweight models we need mappings between domain classes and models.

Base mapper

Instead of writing mapper for each type-pair mappings you can avoid writing many lines of repeating code when using AutoMapper. Here is my base class for mappers.

public abstract class BaseMapper<T, U> where T : BaseEntity where U : BaseDto, new()
{
    protected IMappingExpression<U, T> DtoToDomainMapping { get; private set; }
    protected IMappingExpression<T, U> DomainToDtoMapping { get; private set; }
 
    public BaseMapper()
    {
        DomainToDtoMapping = Mapper.CreateMap<T, U>();
 
        var mex = Mapper.CreateMap<U, T>()
                        .ForMember(m => m.Id, m => m.Ignore());
 
        var refProperties = from p in typeof(T).GetProperties()
                            where p.PropertyType.BaseType == typeof(BaseEntity)
                            select p;
 
        foreach (var prop in refProperties)
        {
            mex.ForMember(prop.Name, m => m.Ignore());
        }
 
        Mapper.CreateMap<PagedResult<T>, PagedResult<U>>()
              .ForMember(m => m.Results, m => m.Ignore());
    }
 
    public U MapToDto(T instance)
    {
        if (instance == null)
            return null;
 
        var dto = new U();
 
        Mapper.Map(instance, dto);
 
        return dto;
    }
 
    public IList<U> MapToDtoList(IList<T> list)
    {
        if (list == null)
            return new List<U>();
 
        var dtoList = new List<U>();
 
        Mapper.Map(list, dtoList);
 
        return dtoList;
    }
 
    public PagedResult<U> MapToDtoPagedResult(PagedResult<T> pagedResult)
    {
        if (pagedResult == null)
            return null;
 
        var dtoResult = new PagedResult<U>();
        Mapper.Map(pagedResult, dtoResult);
        Mapper.Map(pagedResult.Results, dtoResult.Results);
 
        return dtoResult;
    }
 
    public void MapFromDto(U dto, T instance)
    {
        Mapper.Map(dto, instance);
    }
}

It does all the dirty work and in most cases it provides all functionality I need for type-pair mapping.

In constructor I define mappings for domain class to model and model to domain class mapping. Also I define mapping for PagedResult – this is the class I use for paged results. If inheriting classes need to modify mappings then they can access protected properties.

Also notice how I play with domain base class: the code avoids situations where AutoMapper may overwrite ID-s and properties that extend domain base class. When you start using mapping then you very soon find out how bad mess AutoMapper can create if you don’t use it carefully.

Methods of mapper base:

  • MapToDto – takes domain object and returns mapped DTO.
  • MapToDtoList – takes list of domain objects and returns list of DTO-s.
  • MapToDtoPagedResult – takes paged result with domain objects and returns paged result with DTO-s.
  • MapFromDto – maps DTO properties to domain object.

If you need more mapping helpers you can upgrade my class with your own code.

Example

To give you better idea about how to extend my base class here is the example.

public class FillLevelMapper : BaseMapper<FillLevel, FillLevelDto>
{
    public FillLevelMapper()
    {
        DomainToDtoMapping.ForMember(
            l => l.Grade, m => m.MapFrom(l => l.Grade.GradeNo)
        );
    }
}

Mapper classes extend from BaseMapper and add their specifics to mappings that base mapper doesn’t provide.

Conclusion

Mapping is also one repeating patterns in many systems. After building some mappers from zero you start recognizing parts they have in common. I was able to separate common operations of my mappers to base class using generics and AutoMapper. Mapper classes are very thin and therefore also way easier to test. AutoMapper makes a lot of dirty work for me that is otherwise time consuming to code. Of course, by all it’s power you must use AutoMapper carefully so it doesn’t do too much work.

Liked this post? Empower your friends by sharing it!

View Comments (5)

  • Needs some more usage examples, like perhaps show a sample mvc controller using your mapping.

  • Great article. I'm looking at creating a version of this so that I can initialize all BaseMapper types into IoC. One thing I did notice is that DtoToDomainMapping is never set. After the line

    foreach (var prop in refProperties)
    {
    mex.ForMember(prop.Name, m => m.Ignore());
    }

    DtoToDomainMapping = mex is required to set DtoToDomainMapping

  • It should work with current AutoMapper too. As Mapper.Map() is deprecated and removed from AutoMapper we must use mapping profiles. It's possible to use this class as a base class for mapping profiles. We have to extend it from Profile class then.

Related Post