Я хотел бы сказать, что вы повторно используете термин 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
и an AddEmployeeMailingAddress
в списке. Пользователь нажимает «Сохранить», чтобы зафиксировать изменения, и вы отправляете список из двух объектов, каждый из которых содержит только информацию, необходимую для выполнения каждого действия.
Вам понадобится более интеллектуальный ModelBinder, чем тот, который используется по умолчанию, но хороший сериализатор JSON должен иметь возможность позаботиться о сопоставлении объектов действий на стороне клиента с объектами на стороне сервера. На стороне сервера (если вы находитесь в двухуровневой среде) легко могут быть методы, завершающие действие в модели, с которой они работают. Таким образом, действие контроллера заканчивается просто получением идентификатора экземпляра модели для извлечения и списка действий, которые необходимо выполнить над ним. Или в действиях есть идентификатор, чтобы они были разделены.
Так что, возможно, что-то подобное будет реализовано на стороне сервера:
public interface IUserAction<TModel>
{
long ModelId { get; set; }
IEnumerable<string> Validate(TModel model);
void Complete(TModel model);
}
[Transaction]
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
var errors = new List<string>();
foreach( var action in actions )
{
var employee = _employeeRepository.Get(action.ModelId);
errors.AddRange(action.Validate(employee));
}
foreach( var action in editModel.UserActions )
{
var employee = _employeeRepository.Get(action.ModelId);
action.Complete(employee);
_employeeRepository.Update(employee);
}
}
Это действительно делает действие обратной отправки довольно универсальным, поскольку вы полагаетесь на свой ModelBinder, чтобы получить правильный экземпляр IUserAction и ваш экземпляр IUserAction, чтобы либо выполнить правильную логику, либо (что более вероятно) вызвать модель с информацией.
Если бы вы были в трехуровневой среде, IUserAction можно было бы просто сделать простыми DTO, которые будут сниматься через границу и выполняться аналогичным методом на уровне приложения. В зависимости от того, как вы делаете этот слой, он может быть очень легко разделен и по-прежнему оставаться в транзакции (что приходит на ум, так это запрос / ответ Агаты и использование преимуществ карты идентичности DI и NHibernate).
В любом случае, я уверен, что это не идеальная идея, для управления потребуется некоторый JS на стороне клиента, и я еще не смог создать проект, чтобы увидеть, как он разворачивается, но в сообщении пытались подумать о том, как добраться туда и обратно, так что я подумал, что выскажу свои мысли. Я надеюсь, что это поможет, и я хотел бы услышать о других способах управления взаимодействиями.