Welcome to EMC Consulting Blogs Sign in | Join | Help

Owain Wraggs' Blog

I have now left EMC Consulting, you can subscribe to my new blog here: http://blog.endjin.com/author/owain-wragg/

AutoMapper Profiles

I’ve now worked on several ASP.NET MVC projects which utilise AutoMapper (http://automapper.codeplex.com/) to perform the mapping between view models and domain objects and vice versa. AutoMapper works brilliantly and I would recommend it to anyone who needs to perform mapping between different objects.

The approach I’ve always taken when using AutoMapper is to create a mapper class which is responsible for mapping one type to another type. So for example given the two entities below

    public class Address

    {

        public string FirstLine { get; set; }

 

        public string Country { get; set; }

 

        public string PostCode { get; set; }

    }

 

And

    public class AddressViewModel

    {

        [DisplayName("Address Line One")]

        public string PersonAddressLineOne { get; set; }

 

        [DisplayName("Country Of Residence")]

        public string PersonCountryOfResidence { get; set; }

 

        [DisplayName("Post Code")]

        public string PostCode { get; set; }

    }

 

If I wanted to map from an Address entity to an AddressViewModel entity I would create an AddressToAddressViewModelMapper class which is responsible for performing the mapping (shown below)

    public class AddressToAddressViewModelMapper : IMapper<Address, AddressViewModel>

    {

        public AddressToAddressViewModelMapper()

        {

            AutoMapper.Mapper.CreateMap<Address, AddressViewModel>()

                .ForMember(x => x.PersonAddressLineOne, opt => opt.MapFrom(source => source.FirstLine))

                .ForMember(x => x.PersonCountryOfResidence, opt => opt.MapFrom(source => source.Country));

        }

 

        public AddressViewModel Map(Address source)

        {

            var destination = AutoMapper.Mapper.Map<Address, AddressViewModel>(source);

            return destination;

        }

    }

 

The issue I have with this approach is that even for a small number of entities you have to create a large number of mapper classes and this bloats your solution and further to this if you are using dependency injection you end up injecting a large number of mappers into each controller increasing the complexity of both the code and any unit tests that you have.

To illustrate how complex such mappers can get, I will show an example of mapping from a simple domain model to an equally simple view model

Shown below are the domain objects

    public class Person

    {

        public int Id { get; set; }

 

        public string Firstname { get; set; }

 

        public string Surname { get; set; }

 

        public Address Address { get; set; }

 

        public List<Note> Notes { get; set; }

    }

 

    public class Address

    {

        public string FirstLine { get; set; }

 

        public string Country { get; set; }

 

        public string PostCode { get; set; }

    }

 

    public class Note

    {

        public int Id { get; set; }

 

        public string Text { get; set; }

    }

 

Which need to be mapped to the following view model objects

    public class PersonViewModel

    {

        [DisplayName("Person Id")]

        public int Id { get; set; }

 

        [DisplayName("Person Firstname")]

        public string Firstname { get; set; }

 

        [DisplayName("Person surname")]

        public string Surname { get; set; }

 

        public AddressViewModel Address { get; set; }

 

        public List<NoteViewModel> Notes { get; set; }

    }

 

    public class AddressViewModel

    {

        [DisplayName("Address Line One")]

        public string PersonAddressLineOne { get; set; }

 

        [DisplayName("Country Of Residence")]

        public string PersonCountryOfResidence { get; set; }

 

        [DisplayName("Post Code")]

        public string PostCode { get; set; }

    }

 

    public class NoteViewModel

    {

        [DisplayName("Note Id")]

        public int Id { get; set; }

 

        [DisplayName("Note Text")]

        public string Text { get; set; }

    }

 

To perform the mapping we need to create a mapper class for each mapping that is performed, in this example mappers that perform the following mappings are required

  • Person to PersonViewModel
  • Address to AddressViewModel
  • Note to NoteViewModel

The mapping classes are shown below, note that when performing a mapping from a Person domain object the mapper responsible for performing the mapping will need to be aware of the mappers for the other complex types that the Person domain object contains (Address and Note)

    public class PersonToPersonViewModelMapper : IMapper<Person, PersonViewModel>

    {

        private NoteToNoteViewModelMapper _noteToNoteViewModelMapper = new NoteToNoteViewModelMapper();

        private AddressToAddressViewModelMapper _addressToAddressViewModelMapper = new AddressToAddressViewModelMapper();

 

        public PersonToPersonViewModelMapper()

        {

            AutoMapper.Mapper.CreateMap<Person, PersonViewModel>();

        }

 

        public PersonViewModel Map(Person source)

        {

            var destination = AutoMapper.Mapper.Map<Person, PersonViewModel>(source);

 

            destination.Address = source.Address.MapUsing(_addressToAddressViewModelMapper);

 

            destination.Notes = source.Notes.MapAllUsing(_noteToNoteViewModelMapper);

 

            return destination;

        }

    }

 

    public class AddressToAddressViewModelMapper : IMapper<Address, AddressViewModel>

    {

        public AddressToAddressViewModelMapper()

        {

            AutoMapper.Mapper.CreateMap<Address, AddressViewModel>()

                .ForMember(x => x.PersonAddressLineOne, opt => opt.MapFrom(source => source.FirstLine))

                .ForMember(x => x.PersonCountryOfResidence, opt => opt.MapFrom(source => source.Country));

        }

 

        public AddressViewModel Map(Address source)

        {

            var destination = AutoMapper.Mapper.Map<Address, AddressViewModel>(source);

            return destination;

        }

    }

 

    public class NoteToNoteViewModelMapper : IMapper<Note, NoteViewModel>

    {

        public NoteToNoteViewModelMapper()

        {

            AutoMapper.Mapper.CreateMap<Note, NoteViewModel>();

        }

 

        public NoteViewModel Map(Note source)

        {

            var destination = AutoMapper.Mapper.Map<Note, NoteViewModel>(source);

            return destination;

        }

    }

 

The code below shows the code in the controller that actually performs the mapping

        public ActionResult Index()

        {

            var person = _personTasks.GetPerson(23);

 

            var mapper = new PersonToPersonViewModelMapper();

            var personViewModel = mapper.Map(person);

 

            return View(personViewModel);

        }

 

As you can see from the code above even for a simple example it can get complicated quite quickly. Luckily AutoMapper offers an alternative approach – Profiles.

Profiles allow you to group mapping configuration together, for example you could group mapping configuration by area or controller. The benefit of using profiles is that it removes the need for individual mapper classes, therefore reducing the complexity of your solution. In addition to this the act of creating the mapper configuration is quite a costly operation and if this logic in encapsulated in individual mapper classes this configuration is performed (unnecessarily) every time the mapper class is created but with profiles you initialize them once at application start up therefore improving the performance of your application.

For the purposes of this example I created two profiles one responsible for containing the mapping configuration when mapping from domain objects to view models and the other for mapping from view models to domain objects. The two profile classes are shown below

    public class DomainToViewModelMappingProfile : Profile

    {

        public override string ProfileName

        {

            get { return "DomainToViewModelMappings"; }

        }

 

        protected override void Configure()

        {

            Mapper.CreateMap<Person, PersonViewModel>();

 

            Mapper.CreateMap<Address, AddressViewModel>()

                .ForMember(x => x.PersonAddressLineOne, opt => opt.MapFrom(source => source.FirstLine))

                .ForMember(x => x.PersonCountryOfResidence, opt => opt.MapFrom(source => source.Country));

 

            Mapper.CreateMap<Note, NoteViewModel>();

        }

    }

 

And

     public class ViewModelToDomainMappingProfile : Profile

    {

        public override string ProfileName

        {

            get { return "ViewModelToDomainMappings"; }

        }

 

        protected override void Configure()

        {

            Mapper.CreateMap<PersonViewModel, Person>();

 

            Mapper.CreateMap<AddressViewModel, Address>()

                .ForMember(x => x.FirstLine, opt => opt.MapFrom(source => source.PersonAddressLineOne))

                .ForMember(x => x.Country, opt => opt.MapFrom(source => source.PersonCountryOfResidence));

 

            Mapper.CreateMap<NoteViewModel, Note>();

        }

    }

 

To register the profiles you need to initialize AutoMapper at application start up so that it knows about the profiles. To do this I’ve created a helper class (shown below) which is responsible for initializing AutoMapper

    public class AutoMapperConfiguration

    {

        public static void Configure()

        {

            Mapper.Initialize(x =>

            {

                x.AddProfile<DomainToViewModelMappingProfile>();

                x.AddProfile<ViewModelToDomainMappingProfile>();

            });

        }

    }

 

Then I call the Configure method from the Application_Start method of the Global.asax

        protected void Application_Start()

        {

            // Initialize automapper

            AutoMapperConfiguration.Configure();

 

            AreaRegistration.RegisterAllAreas();

 

            RegisterRoutes(RouteTable.Routes);

 

            // Setup spark view engine

            var settings = new SparkSettings();

 

            settings

                .AddNamespace("System")

                .AddNamespace("System.Collections.Generic")

                .AddNamespace("System.Linq")

                .AddNamespace("System.Web.Mvc")

                .AddNamespace("System.Web.Mvc.Html");

 

            settings.AutomaticEncoding = true;

 

            ViewEngines.Engines.Add(new SparkViewFactory(settings));

        }

 

To perform a mapping you simply to call the AutoMapper API and ask it to perform a mapping, example below

        public ActionResult Index()

        {

            var person = _personTasks.GetPerson(23);

 

            var personViewModel = AutoMapper.Mapper.Map<Person, PersonViewModel>(person);

 

            return View(personViewModel);

        }

 

Obviously to improve testability you could encapsulate AutoMapper within a custom class which you supply to the controller via dependency injection.

Attach to this post is the example solution.
Published Wednesday, December 15, 2010 4:02 PM by Owain.Wragg
Attachment(s): AutoMapperProfiles.zip

Comments

 

Simon Evans' Blog said:

The .NET 4 version of the Entity Framework (EF) brought several major improvements over the first version

January 31, 2011 4:43 PM
 

Simon Evans' Blog said:

Back in August last year I blogged about a pattern I’d created for consuming data from Windows Azure

February 3, 2011 2:46 PM
Anonymous comments are disabled
Powered by Community Server (Personal Edition), by Telligent Systems