Как преобразовать editmodel/postmodel в модель домена
В проекте ASP.NET MVC мы используем AutoMapper для отображения из доменной модели в viewmodel - и иногда при этом выравниваем иерархию. Это работает как очарование и делает логику рендеринга наших представлений очень скудной и простой.
Путаница начинается, когда мы хотим пойти другим путем от viewmodel (или postmodel или editmodel) к модели предметной области, особенно при обновлении объектов. Мы не можем использовать автоматическое / двустороннее сопоставление, потому что:
- мы должны были бы развернуть уплощенную иерархию
- все свойства в доменной модели должны быть изменяемыми / иметь общедоступные сеттеры
- изменения, происходящие из представления, не всегда являются плоскими свойствами, отображаемыми обратно в домен, но иногда необходимо вызывать методы типа "
ChangeManagerForEmployee()
" или похожие.
Это также описано в статье Джимми Богардса: случай двустороннего сопоставления в AutoMapper, но решение этого не описано подробно, только то, что они идут:
От EditModel до CommandMessages - переход от слабо типизированной EditModel к строго типизированным выделенным сообщениям. Одна EditModel может генерировать полдюжины сообщений.
В аналогичном вопросе SO есть ответ Mark Seemann, где он упоминает, что
Мы используем абстрактные мапперы и сервисы для отображения PostModel в объект Domain.
но детали - концептуальная и техническая реализация - не учтены.
Наша идея сейчас заключается в том, чтобы:
- Получить FormCollection в методе действия контроллера
- Получите исходную модель домена и выровняйте ее для viewModelOriginal и viewModelUpdated
- Слияние FormCollection с viewModelUpdated с использованием
UpdateModel()
- Используйте некоторый универсальный вспомогательный метод для сравнения viewModelOriginal с viewModelUpdated
- Либо А) Создать 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
}
}
Это на самом деле просто перемещение "обработки" формы из действия контроллера в отдельные специализированные обработчики.
Но я бы действительно пошел по этому пути, только если действия контроллера стали бы пушистыми. В противном случае просто примите форму и при необходимости сделайте соответствующие обновления для моделей домена.