Где должны появляться заявления "CreateMap"?

Я часто использую AutoMapper для сопоставления объектов Model (Domain) с объектами ViewModel, которые затем потребляются моими представлениями, в шаблоне Model/View/View-Model.

Это включает в себя множество операторов Mapper.CreateMap, которые все должны быть выполнены, но должны выполняться только один раз в жизненном цикле приложения.

Технически, тогда я должен хранить их все в статическом методе где-нибудь, который вызывается из моего метода Application_Start() (это приложение ASP.NET MVC).

Однако, кажется, неправильно группировать множество различных проблем отображения вместе в одном центральном месте.

Особенно, когда код сопоставления становится сложным и включает в себя форматирование и другую логику.

Есть ли лучший способ организовать код сопоставления, чтобы он находился близко к ViewModel, который это касается?

(Я придумал одну идею - имея метод CreateMappings на каждом ViewModel и в BaseViewModel, вызывая этот метод при создании экземпляра. Однако, поскольку метод следует вызывать только один раз в жизненном цикле приложения, ему нужно некоторое дополнительную логику для кэширования списка типов ViewModel, для которых был вызван метод CreateMappings, а затем только вызывать его, если необходимо, для ViewModels, которые не входят в этот список.)

Ответ 1

Если вы используете профили, вы можете разместить там все свои вызовы "CreateMap". Кроме того, вы можете создать статический класс начальной загрузки, который содержит вашу конфигурацию, а часть запуска просто вызовет загрузчик.

Ответ 2

Если вы действительно не хотите использовать загрузчик, то по крайней мере статический конструктор - это простой способ обеспечить, чтобы ваш CreateMap вызывался не более одного раза. (С меньшим количеством беспорядков и большей резьбой, чем ответ Джонатона.)

public class AccountController : Controller 
{
    static AccountController()
    {
        Mapper.CreateMap<Models.User, ViewModels.UserProfile>();
        Mapper.CreateMap<Models.User, ViewModels.ChangePassword>();
    }
}

Ответ 3

ОК, как я это делаю сейчас:

Я добавляю некоторую логику в конструктор моего BaseController, который запускает метод CreateMappings, но только один раз для типа контроллера:

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

    protected virtual void CreateMappings() { }
}

В каждом конкретном контроллере я использую CreateMappings для объявления сопоставлений для всех моделей /ViewModels, относящихся к этому контроллеру.

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

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

Я также нашел несколько интересных альтернатив с участием атрибутов здесь и здесь, однако они ударили меня как немного усложненный.