Использование функций ViewModels для POST в MVC элегантно
В настоящее время я передаю свои объекты домена в свои представления и привязываю непосредственно к ним из POST. Все говорят, что это плохо, поэтому я пытаюсь добавить концепцию ViewModel.
Однако я не могу найти способ сделать это очень изящно, и я хотел бы знать, какие решения других людей не должны заканчиваться очень грязным действием контроллера.
типичный процесс для некоторых функций "добавить человека" выглядит следующим образом:
- сделать запрос GET для представления, представляющего пустую модель просмотра Person
- post back (in) действительные данные
Контроллер
- связывает опубликованные данные с моделью просмотра человека
- Если сбой привязки, мне нужно выполнить то же действие, что и в (1), но с некоторыми данными, а не с пустым объектом и ошибками.
- Если привязка завершилась, мне нужно отобразить свойства из виртуальной машины на реальную модель
- проверить модель
- если проверка прошла: сохранить человека, зафиксировать, сопоставить данные пользователей с виртуальной машиной отображения и вернуть ее в виде
- Если проверка не выполнена, выполните те же действия, что и в (1), но с некоторыми данными и ошибками
Выполнение всего этого в действии контроллера (игнорирование GET), конечно же, не является SRP или DRY.
Я пытаюсь думать о способе нарушить этот процесс, чтобы он выполнял SRP, является чистым, модульным и, прежде всего, проверяемым.
Каковы решения людей для этого?
Я экспериментировал с пользовательскими обработчиками-action-invokers, чтобы разделить проблемы на отдельные методы, умные модели-строчки и просто грубую силу, но я еще не нашел решения в довольстве.
P.S. поскольку он добавляет столько сложностей, убедить меня, почему мне даже нужно беспокоиться
Ответы
Ответ 1
Я почувствовал такой же дискомфорт. Мой единственный способ сделать это:
- Создание связующего для привязки и проверки модели представления
- Создайте связующее, чтобы получить объект из базы данных (или просто сделать это в контроллере).
- Вызов унаследованного метода сохранения в суперклассе. Этот метод принимает viewmodel и объект, который будет обновлен, и выполняет всю работу, указанную вами на ваших шагах.
Метод действия выглядит следующим образом:
public ActionResult Whatever(TViewModel viewModel, TEntity entity)
{
return Save(viewModel, entity);
}
Базовый контроллер имеет общее определение, например:
public abstract BaseController<TEntity, TViewModel>
where TEntity : Entity
where TViewModel : ViewModel
Конструктор имеет две зависимости: одну для репозитория объектов и другую для сопоставления модели, например:
protected BaseController(IRepository<TEntity> repository, IMapper<TEntity, TViewModel> mapper)
После этого вы можете записать защищенный метод Save, который можно вызвать из действий контроллера в подклассе, например:
protected ActionResult Save(TViewModel viewModel, TEntity entity)
{
if (!ModelState.IsValid)
return View(viewModel);
_mapper.Map(viewModel, entity);
if (!entity.IsValid)
{
// add errors to model state
return View(viewModel);
}
try
{
_repository.Save(entity);
// either redirect with static url or add virtual method for defining redirect in subclass.
}
catch (Exception)
{
// do something here with the exception
return View(viewModel);
}
}
Что касается проверки, вы можете протестировать метод сохранения в действительных/недопустимых моделях и объектах просмотра. Вы можете протестировать реализацию mapper модели, действительное состояние модели представления и действительное состояние объекта отдельно.
Предоставив базовый контроллер общий, вы можете повторить этот шаблон для каждой компиляции entity/viewmodel в своем домене, если вы создаете множество контроллеров для выполнения того же самого.
Мне очень интересно услышать, что другие говорят об этом. Большой вопрос.
Ответ 2
Шаблон MVVM (ViewModel) определенно тот, на который нужно пойти, у меня был аналогичный вопрос о POSTing обратно к действию несколько дней назад - вот ссылка: MVVM и ModelBinders в ASP.NET MVC Framework
В результате вы можете использовать атрибут Bind, чтобы отправить обратно сложный тип, который вы хотите.
Ответ 3
У меня есть много хороших решений в приложении asp.net mvc sample, которое находится в загрузке valueinjecter (mapper, который я использую для отображения ViewModels в/из Entities, вы также можете отобразить FormCollection/Request to Entities)
здесь один:
public class TinyController :Controller
{
private readonly IModelBuilder<Person, PersonViewModel> modelBuilder;
public TinyController()
{
modelBuilder = new PersonModelBuilder();
}
public ActionResult Index()
{
return View(modelBuilder.BuildModel(new PersonRepository().Get()));
}
[HttpPost]
public ActionResult Index(PersonViewModel model)
{
if (!ModelState.IsValid)
return View(modelBuilder.RebuildModel(model));
var entity = modelBuilder.BuildEntity(model);
...
//save it or whatever
}
}