Как преобразовать editmodel/postmodel в модель домена

В проекте ASP.NET MVC мы используем AutoMapper для отображения из доменной модели в viewmodel - и иногда при этом выравниваем иерархию. Это работает как очарование и делает логику рендеринга наших представлений очень скудной и простой.

Путаница начинается, когда мы хотим пойти другим путем от viewmodel (или postmodel или editmodel) к модели предметной области, особенно при обновлении объектов. Мы не можем использовать автоматическое / двустороннее сопоставление, потому что:

  1. мы должны были бы развернуть уплощенную иерархию
  2. все свойства в доменной модели должны быть изменяемыми / иметь общедоступные сеттеры
  3. изменения, происходящие из представления, не всегда являются плоскими свойствами, отображаемыми обратно в домен, но иногда необходимо вызывать методы типа "ChangeManagerForEmployee()" или похожие.

Это также описано в статье Джимми Богардса: случай двустороннего сопоставления в AutoMapper, но решение этого не описано подробно, только то, что они идут:

От EditModel до CommandMessages - переход от слабо типизированной EditModel к строго типизированным выделенным сообщениям. Одна EditModel может генерировать полдюжины сообщений.

В аналогичном вопросе SO есть ответ Mark Seemann, где он упоминает, что

Мы используем абстрактные мапперы и сервисы для отображения PostModel в объект Domain.

но детали - концептуальная и техническая реализация - не учтены.

Наша идея сейчас заключается в том, чтобы:

  1. Получить FormCollection в методе действия контроллера
  2. Получите исходную модель домена и выровняйте ее для viewModelOriginal и viewModelUpdated
  3. Слияние FormCollection с viewModelUpdated с использованием UpdateModel()
  4. Используйте некоторый универсальный вспомогательный метод для сравнения viewModelOriginal с viewModelUpdated
  5. Либо А) Создать CommandMessages а-ля Джимми Богард, либо Б) Мутировать различия непосредственно в модель предметной области с помощью свойств и методов (возможно, сопоставляя свойства 1-1 напрямую через AutoMapper)

Может кто-нибудь привести некоторые примеры того, как они переходят из FormCollection через editmodel/postmodel в модель предметной области? "CommandMessages" или "абстрактные средства отображения и службы"?

4 ответа

Я использую следующий шаблон:

[HttpPost]
public ActionResult Update(UpdateProductViewModel viewModel)
{
    // fetch the domain model that we want to update
    Product product = repository.Get(viewModel.Id);

    // Use AutoMapper to update only the properties of this domain model
    // that are also part of the view model and leave the other properties unchanged
    AutoMapper.Map<UpdateProductViewModel, Product>(viewModel, product);

    // Pass the domain model with updated properties to the DAL
    repository.Update(product);

    return RedirectToAction("Success");
}

Возможно, вы захотите рассмотреть CQRS(Разделение ответственности по командным запросам - я думаю, что это может быть концепция, которую вы упустили), возможно, даже с Event Sourcing.

В основном это практика отделения логики чтения от источника данных и записи в источник данных, что может даже означать наличие разных моделей данных для чтения и записи.

Это может быть хорошим местом для начала: http://abdullin.com/cqrs/

Здесь есть некоторые сходства с тем, что я делал. Моя иерархия моделей представлений только несколько сглажена по сравнению с ее эквивалентами объектных объектов, но мне приходится иметь дело с вызовом явных методов обслуживания при сохранении для таких вещей, как добавление в дочерние коллекции, изменение важных значений и т. Д., А не просто обратное сопоставление. Я также должен сравнить до и после снимков.

Мое сохранение - Ajax, опубликованный как JSON для действия MVC, и вводит это действие магическим образом обратно в структуру модели представления MVC. Затем я использую AutoMapper для преобразования модели представления верхнего уровня и ее потомков обратно в эквивалентную доменную структуру. Я определил ряд пользовательских AutoMapper ITypeConverters для тех случаев, когда на клиенте был добавлен новый дочерний элемент (я использую Knockout.js), и мне нужно вызвать явный метод обслуживания. Что-то вроде:

            foreach (ChildViewModel childVM in viewModel.Children)
        {
            ChildDomainObject childDO = domainObject.Children.Where(cdo => cdo.ID.Equals(childVM.ID))).SingleOrDefault();
            if (childDO != null)
            {
                Mapper.Map<ChildViewModel, ChildDomainObject>(childVM, childDO);
            }
            else
            {
                MyService.CreateChildDO(someData, domainObject); // Supplying parent
            }
        }

Я делаю аналогичные вещи для удалений, и этот процесс довольно хорошо распространяется по всей структуре. Я предполагаю, что с плоской структурой может быть проще или сложнее работать - у меня есть AbstractDomainViewModel с идентификатором, с которым я выполняю приведенное выше сопоставление, что помогает.

Мне нужно делать сравнения до и после обновления, потому что мой сервисный уровень вызывает проверку триггера, которая может повлиять на другие части графа объектов, и это диктует, какой JSON мне нужно вернуть в качестве ответа Ajax. Я забочусь только об изменениях, которые имеют отношение к пользовательскому интерфейсу, поэтому я преобразую сохраненный объект домена обратно в новую модель представления, а затем использую вспомогательный метод для сравнения двух моделей представления, используя комбинацию ручных предварительных проверок и отражений.

Вариант C: Поместите все это в действие контроллера. Затем, если это становится проблематичным, разложите его на службы (абстрактные средства отображения) или сообщения-как-методы (способ сообщения команды).

Путь сообщения команды:

public ActionResult Save(FooSaveModel model) {
    MessageBroker.Process(model);

    return RedirectToAction("List");
}

И процессор:

public class FooSaveModelProcessor : IMessageHandler<FooSaveModel> {

    public void Process(FooSaveModel message) {
        // Message handling logic here
    }

}

Это на самом деле просто перемещение "обработки" формы из действия контроллера в отдельные специализированные обработчики.

Но я бы действительно пошел по этому пути, только если действия контроллера стали бы пушистыми. В противном случае просто примите форму и при необходимости сделайте соответствующие обновления для моделей домена.

Другие вопросы по тегам