Каждая статья в Интернете об использовании ViewModels и Automapper дает рекомендации по отображению направления «Контроллер -> Просмотр». Вы берете модель предметной области вместе со всеми списками выбора в одну специализированную модель представления и передаете ее представлению. Это ясно и нормально.
У представления есть форма, и в конце концов мы находимся в действии POST. Здесь все связующие модели выходят на сцену вместе с [очевидно] другой моделью представления, которая [очевидно] связана с исходной моделью представления, по крайней мере, в части соглашений об именах ради привязки и проверки.
Как вы сопоставите это с вашей моделью домена?
Пусть это будет действие вставки, мы могли бы использовать тот же Automapper. Но что, если это было действие обновления? Нам нужно получить объект домена из репозитория, обновить его свойства в соответствии со значениями в ViewModel и сохранить в репозитории.
ПРИЛОЖЕНИЕ 1 (9 февраля 2010 г.): Иногда недостаточно присвоить свойства модели. Должны быть предприняты некоторые действия против модели предметной области в соответствии со значениями модели представления. Т.е. некоторые методы должны вызываться в модели предметной области. Вероятно, должен быть своего рода уровень службы приложений, который стоит между контроллером и доменом для обработки моделей представления ...
Как организовать этот код и где его разместить для достижения следующих целей?
- держать контроллеры тонкими
- уважать практику SoC
- следовать принципам доменно-ориентированного дизайна
- быть СУХИМ
- продолжение следует ...
источник
Такие инструменты, как AutoMapper, можно использовать для обновления существующего объекта данными из исходного объекта. Действие контроллера для обновления может выглядеть так:
[HttpPost] public ActionResult Update(MyViewModel viewModel) { MyDataModel dataModel = this.DataRepository.GetMyData(viewModel.Id); Mapper<MyViewModel, MyDataModel>(viewModel, dataModel); this.Repostitory.SaveMyData(dataModel); return View(viewModel); }
Помимо того, что видно во фрагменте выше:
Действие контроллера довольно тонкое, и проблемы разделены: проблемы сопоставления решаются в конфигурации AutoMapper, проверка выполняется с помощью ModelBinder, а доступ к данным - с помощью репозитория.
источник
Я хотел бы сказать, что вы повторно используете термин ViewModel для обоих направлений взаимодействия с клиентом. Если вы прочитали достаточно кода ASP.NET MVC в реальной жизни, вы, вероятно, заметили различие между ViewModel и EditModel. Я думаю, это важно.
ViewModel представляет всю информацию, необходимую для визуализации представления. Это могут быть данные, которые отображаются в статических неинтерактивных местах, а также данные, предназначенные исключительно для проверки, чтобы решить, что именно отображать. Действие GET контроллера обычно отвечает за упаковку ViewModel для своего View.
EditModel (или, возможно, ActionModel) представляет данные, необходимые для выполнения действия, которое пользователь хотел сделать для этого POST. Итак, EditModel действительно пытается описать действие. Это, вероятно, исключит некоторые данные из ViewModel, и, хотя они связаны, я думаю, важно понимать, что они действительно разные.
Одна идея
Тем не менее, вы можете очень легко получить конфигурацию AutoMapper для перехода от Model -> ViewModel и другую для перехода от EditModel -> Model. Тогда для различных действий контроллера просто необходимо использовать AutoMapper. Черт возьми, EditModel может иметь на себе функции для проверки его свойств на соответствие модели и применения этих значений к самой модели. Он больше ничего не делает, и у вас есть ModelBinders в MVC для сопоставления запроса с EditModel.
Другая идея
Кроме того, что-то, о чем я недавно размышлял, такого рода отрабатывает идею ActionModel, заключается в том, что то, что клиент отправляет вам в ответ, на самом деле является описанием нескольких действий, выполненных пользователем, а не просто одним большим шаром данных. Это, безусловно, потребует некоторого Javascript на стороне клиента для управления, но я думаю, что идея интригует.
По сути, когда пользователь выполняет действия на экране, который вы ему представили, Javascript начнет создавать список объектов действий. Например, возможно, пользователь находится на экране информации о сотруднике. Они обновляют фамилию и добавляют новый адрес, потому что сотрудник недавно был женат. Под обложками это создает объекты a
ChangeEmployeeName
и anAddEmployeeMailingAddress
в списке. Пользователь нажимает «Сохранить», чтобы зафиксировать изменения, и вы отправляете список из двух объектов, каждый из которых содержит только информацию, необходимую для выполнения каждого действия.Вам понадобится более интеллектуальный ModelBinder, чем тот, который используется по умолчанию, но хороший сериализатор JSON должен иметь возможность позаботиться о сопоставлении объектов действий на стороне клиента с объектами на стороне сервера. На стороне сервера (если вы находитесь в двухуровневой среде) легко могут быть методы, завершающие действие в модели, с которой они работают. Таким образом, действие контроллера заканчивается просто получением идентификатора экземпляра модели для извлечения и списка действий, которые необходимо выполнить над ним. Или в действиях есть идентификатор, чтобы они были разделены.
Так что, возможно, что-то подобное будет реализовано на стороне сервера:
public interface IUserAction<TModel> { long ModelId { get; set; } IEnumerable<string> Validate(TModel model); void Complete(TModel model); } [Transaction] //just assuming some sort of 2-tier with transactions handled by filter public ActionResult Save(IEnumerable<IUserAction<Employee>> actions) { var errors = new List<string>(); foreach( var action in actions ) { // relying on ORM's identity map to prevent multiple database hits var employee = _employeeRepository.Get(action.ModelId); errors.AddRange(action.Validate(employee)); } // handle error cases possibly rendering view with them foreach( var action in editModel.UserActions ) { var employee = _employeeRepository.Get(action.ModelId); action.Complete(employee); // against relying on ORMs ability to properly generate SQL and batch changes _employeeRepository.Update(employee); } // render the success view }
Это действительно делает действие обратной отправки довольно универсальным, поскольку вы полагаетесь на свой ModelBinder, чтобы получить правильный экземпляр IUserAction и ваш экземпляр IUserAction, чтобы либо выполнить правильную логику, либо (что более вероятно) вызвать модель с информацией.
Если бы вы были в трехуровневой среде, IUserAction можно было бы просто сделать простыми DTO, которые будут сниматься через границу и выполняться аналогичным методом на уровне приложения. В зависимости от того, как вы делаете этот слой, он может быть очень легко разделен и по-прежнему оставаться в транзакции (что приходит на ум, так это запрос / ответ Агаты и использование преимуществ карты идентичности DI и NHibernate).
В любом случае, я уверен, что это не идеальная идея, для управления потребуется некоторый JS на стороне клиента, и я еще не смог создать проект, чтобы увидеть, как он разворачивается, но в сообщении пытались подумать о том, как добраться туда и обратно, так что я подумал, что выскажу свои мысли. Я надеюсь, что это поможет, и я хотел бы услышать о других способах управления взаимодействиями.
источник
Вам не нужно сопоставлять модель просмотра с доменом, потому что ваша модель просмотра может быть создана больше, чем модель домена. Модели просмотра оптимизированы для экрана (ui) и отличаются от модели предметной области.
http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/
источник