Todos os artigos encontrados na Internet sobre o uso de ViewModels e Automapper fornecem as diretrizes do mapeamento de direção "Controlador -> Visualização". Você pega um modelo de domínio junto com todas as Listas de Seleção em um ViewModel especializado e o passa para a visualização. Isso é claro e bom.
A view possui um formulário e eventualmente estamos na ação POST. Aqui, todos os Model Binders entram em cena junto com [obviamente] outro View Model que está [obviamente] relacionado ao ViewModel original, pelo menos na parte das convenções de nomenclatura para fins de vinculação e validação.
Como você mapeia para o seu modelo de domínio?
Que seja uma ação de inserção, poderíamos usar o mesmo Automapper. Mas e se fosse uma ação de atualização? Temos que recuperar nossa Entidade de Domínio do Repositório, atualizar suas propriedades de acordo com os valores no ViewModel e salvar no Repositório.
ADENDO 1 (9 de fevereiro de 2010): Às vezes, atribuir as propriedades do Model não é suficiente. Deve ser realizada alguma ação contra o Modelo de Domínio de acordo com os valores do Modelo de Visão. Ou seja, alguns métodos devem ser chamados no Modelo de Domínio. Provavelmente, deve haver uma espécie de camada de serviço de aplicativo que fica entre o controlador e o domínio para processar os modelos de visão ...
Como organizar este código e onde colocá-lo para atingir os seguintes objetivos?
- manter os controladores finos
- honrar a prática SoC
- seguir os princípios do Domain-Driven Design
- ser SECO
- continua ...
fonte
Ferramentas como o AutoMapper podem ser usadas para atualizar o objeto existente com dados do objeto de origem. A ação do controlador para atualização pode ser semelhante a:
[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); }
Além do que é visível no snippet acima:
A ação do controlador é muito fina e os interesses são separados: os problemas de mapeamento são tratados na configuração do AutoMapper, a validação é feita pelo ModelBinder e o acesso aos dados pelo Repositório.
fonte
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 umAddEmployeeMailingAddress
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] //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 }
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.
fonte
Você não precisa mapear o modelo de visão para o domínio porque seu modelo de visão pode ser criado mais do que o modelo de domínio. Viewmodels otimizados para tela (ui) e diferentes do modelo de domínio.
http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/
fonte