Eu gostaria de dizer que você reutiliza o termo ViewModel para ambas as direções da interação do cliente. Se você leu código ASP.NET MVC suficiente em liberdade, provavelmente viu a distinção entre ViewModel e EditModel. Eu acho isso importante.
Um ViewModel representa todas as informações necessárias para renderizar uma visualização. Isso pode incluir dados que são renderizados em locais não interativos estáticos e também dados puramente para realizar uma verificação para decidir o que exatamente renderizar. Uma ação GET do controlador é geralmente responsável por empacotar o ViewModel para sua View.
Um EditModel (ou talvez um ActionModel) representa os dados necessários para realizar a ação que o usuário deseja fazer para aquele POST. Portanto, um EditModel está realmente tentando descrever uma ação. Isso provavelmente excluirá alguns dados do ViewModel e, embora relacionados, acho importante perceber que eles são realmente diferentes.
Uma ideia
Dito isso, você poderia facilmente ter uma configuração do AutoMapper para ir de Model -> ViewModel e uma diferente para ir de EditModel -> Model. Então, as diferentes ações do controlador precisam apenas usar o AutoMapper. Inferno, o EditModel poderia ter funções para validar suas propriedades em relação ao modelo e para aplicar esses valores ao próprio Modelo. Ele não está fazendo mais nada e você tem ModelBinders no MVC para mapear a solicitação para o EditModel de qualquer maneira.
Outra ideia
Além disso, algo em que estive pensando recentemente que funciona com a ideia de um ActionModel é que o que o cliente está postando de volta para você é na verdade a descrição de várias ações que o usuário realizou e não apenas um grande conjunto de dados. Isso certamente exigiria algum Javascript do lado do cliente para gerenciar, mas a ideia é intrigante, eu acho.
Essencialmente, conforme o usuário executa ações na tela que você os apresentou, o Javascript começa a criar uma lista de objetos de ação. Um exemplo é possivelmente o usuário estar em uma tela de informações do funcionário. Eles atualizam o sobrenome e adicionam um novo endereço porque o funcionário se casou recentemente. Nos bastidores, isso produz um ChangeEmployeeName
e um AddEmployeeMailingAddress
objetos para uma lista. O usuário clica em 'Salvar' para confirmar as alterações e você envia a lista de dois objetos, cada um contendo apenas as informações necessárias para realizar cada ação.
Você precisaria de um ModelBinder mais inteligente do que o padrão, mas um bom serializador JSON deve ser capaz de cuidar do mapeamento dos objetos de ação do lado do cliente para os do lado do servidor. Os do lado do servidor (se você estiver em um ambiente de 2 camadas) podem facilmente ter métodos que concluem a ação no modelo com o qual trabalham. Assim, a ação Controller acaba obtendo apenas um Id para a instância de Model puxar e uma lista de ações a serem executadas nela. Ou as ações têm o id para mantê-los bem separados.
Então, talvez algo assim seja percebido no lado do servidor:
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);
}
}
Isso realmente torna a ação de postagem de volta bastante genérica, pois você está contando com seu ModelBinder para obter a instância IUserAction correta e sua instância IUserAction para realizar a própria lógica correta ou (mais provavelmente) chamar o Model com as informações.
Se você estivesse em um ambiente de 3 camadas, a IUserAction poderia ser transformada em DTOs simples para serem disparados além do limite e executados em um método semelhante na camada do aplicativo. Dependendo de como você faz essa camada, ela pode ser facilmente dividida e ainda permanecer em uma transação (o que vem à mente é a solicitação / resposta de Agatha e aproveitando o DI e o mapa de identidade do NHibernate).
De qualquer forma, tenho certeza de que não é uma ideia perfeita, exigiria algum JS do lado do cliente para gerenciar, e ainda não consegui fazer um projeto para ver como se desenrola, mas o post estava tentando pensar em como ir e voltar, então resolvi dar minhas opiniões. Espero que ajude e adoraria ouvir sobre outras maneiras de gerenciar as interações.