Я хотел бы сказать, что вы повторно используете термин ViewModel для обоих направлений взаимодействия с клиентом. Если вы прочитали достаточно кода ASP.NET MVC в дикой природе, вы, вероятно, видели различие между ViewModel и EditModel. Я думаю, что это важно.
ViewModel представляет всю информацию, необходимую для визуализации представления. Это может включать данные, которые отображаются в статических неинтерактивных местах, а также данные, предназначенные исключительно для проверки того, что именно следует отображать. Действие Controller GET обычно отвечает за упаковку ViewModel для его View.
EditModel (или, возможно, ActionModel) представляет данные, необходимые для выполнения действия, которое пользователь хотел сделать для этого POST. Таким образом, EditModel действительно пытается описать действие. Это, вероятно, исключит некоторые данные из ViewModel, и хотя я думаю, что это важно, важно понимать, что они действительно разные.
Одна идея
Тем не менее, вы можете очень легко иметь конфигурацию AutoMapper для перехода из Model -> ViewModel и другую конфигурацию из EditModel -> Model. Тогда различные действия контроллера просто нужно использовать AutoMapper. Черт, у EditModel могут быть функции для проверки его свойств по отношению к модели и применения этих значений к самой модели. Он больше ничего не делает, и у вас есть ModelBinder в MVC, чтобы в любом случае отобразить Запрос в EditModel.
Другая идея
Помимо того, о чем я недавно думал, такого рода работа над идеей ActionModel заключается в том, что то, что клиент отправляет вам обратно, на самом деле является описанием нескольких действий, которые выполнил пользователь, а не только одного большого объема данных , Это, конечно, потребовало бы некоторого Javascript на стороне клиента для управления, но я думаю, что эта идея интригует.
По существу, когда пользователь выполняет действия на экране, который вы им представили, Javascript начинает создавать список объектов действия. Например, возможно, пользователь находится на экране информации о сотруднике. Они обновляют фамилию и добавляют новый адрес, потому что сотрудник недавно был женат. Под покровом это создает в списке объекты ChangeEmployeeName
и AddEmployeeMailingAddress
. Пользователь нажимает «Сохранить», чтобы зафиксировать изменения, и вы отправляете список из двух объектов, каждый из которых содержит только информацию, необходимую для выполнения каждого действия.
Вам потребуется более интеллектуальный ModelBinder, чем по умолчанию, но хороший сериализатор JSON должен быть в состоянии позаботиться о сопоставлении объектов действий на стороне клиента с объектами на стороне сервера. На серверной стороне (если вы находитесь в двухуровневой среде) могут быть легко найдены методы, выполняющие действие с той моделью, с которой они работают. Таким образом, действие Controller заканчивается получением идентификатора экземпляра Model для извлечения и списка действий, которые нужно выполнить над ним. Или действия имеют идентификатор в них, чтобы держать их очень отдельно.
Так что, может быть, что-то подобное реализуется на стороне сервера:
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 на стороне клиента, и я пока не смог сделать проект, чтобы увидеть, как он разворачивается, но пост пытался подумать о том, как добраться туда и обратно, так что я подумал, что я буду думать. Я надеюсь, что это поможет, и я хотел бы услышать о других способах управления взаимодействиями.