views:

91

answers:

2

I frequently use AutoMapper to map Model (Domain) objects to ViewModel objects, which are then consumed by my Views, in a Model/View/View-Model pattern.

This involves many 'Mapper.CreateMap' statements, which all must be executed, but must only be executed once in the lifecycle of the application.

Technically, then, I should keep them all in a static method somewhere, which gets called from my Application_Start() method (this is an ASP.NET MVC application).

However, it seems wrong to group a lot of different mapping concerns together in one central location.

Especially when mapping code gets complex and involves formatting and other logic.

Is there a better way to organize the mapping code so that it's kept close to the ViewModel that it concerns?

(I came up with one idea - having a 'CreateMappings' method on each ViewModel, and in the BaseViewModel, calling this method on instantiation. However, since the method should only be called once in the application lifecycle, it needs some additional logic to cache a list of ViewModel types for which the CreateMappings method has been called, and then only call it when necessary, for ViewModels that aren't in that list.)

+2  A: 

If you use profiles, you can place all of your "CreateMap" calls there. Additionally, you can create a static bootstrapper class that contains your configuration, and have the startup piece just call the bootstrapper.

Jimmy Bogard
But that still doesn't solve the problem of having all the mappings for all the different view-models lumped together, when I'd rather have them somehow related to the classes they apply to.
jonathanconway
A: 

OK, the way I'm currently doing it is this:

I add some logic to the constructor of my BaseController, which runs the 'CreateMappings' method, but only once per Controller Type:

public abstract class BaseController : Controller
{    
    public BaseController()
    {
        if (!controllersWithMappingsCreated.Contains(GetType()))
        {
            CreateMappings();
            controllersWithMappingsCreated.Enqueue(GetType());
        }
    }

    protected virtual void CreateMappings() { }
}

In each concrete controller, I use CreateMappings to declare the mappings for all the Models/ViewModels relevant to that controller.

public class AccountController : BaseController
{
    public AccountController() : base() { }

    protected override void CreateMappings()
    { 
        Mapper.CreateMap<Models.User, ViewModels.UserProfile>();
        Mapper.CreateMap<Models.User, ViewModels.ChangePassword>();
    }
}

I also found some interesting alternatives involving Attributes here and here, however they strike me as a bit overcomplicated.

jonathanconway
I'd still have a static bootstrapper class, so you can unittest your mappings.
Martin