MVC - Controlador de Serviço de Comunicação Camada
-
27-10-2019 - |
Pergunta
Na minha ASP.net MVC aplicativo que eu estou usando uma camada de serviço e Repositórios para manter meus controladores fina. Um detalhes típicos somente leitura view parece com isso:
public ActionResult Details(int id)
{
var project = _projectService.GetById(id);
return View(Mapper.Map<Project, ProjectDetails>(project));
}
Camada de Serviço:
public class ProjectService : IProjectService
{
public Project GetById(int id)
{
var project = _projectRepository.GetProject(id);
// do some stuff
return project;
}
}
public class ProjectRepository : IProjectRepository
{
public Project GetProject(int id)
{
return context.Projects.Find(id);
}
}
Mover-se da camada de serviço para o modelo de vista é muito fácil por causa da AutoMapper, que pode achatar as coisas muito facilmente. Movendo o outro direto, a partir do modelo fim de passar para a minha camada de serviço é onde eu me esforço para chegar a uma boa solução.
Em uma situação como uma ação Criar, o que é uma boa abordagem para isso?
[HttpPost]
public ActionResult Create(CreateProjectViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
// TODO
return RedirectToAction("Index");
}
Eu tenho certeza que a camada de serviço não deve saber nada sobre modelos de exibição, mas eu também não acho que AutoMapper funciona bem neste cenário, quer, desde que não é bom em tomar um modelo de plano e tornando-se em um objeto complexo.
O que deve o meu olhar controlador de como se comunicar com a camada de serviço? Eu quero manter o código no controlador o mais leve possível.
Solução
Você pode definir um mapeamento bidirecional e depois ir o contrário:
[HttpPost]
public ActionResult Create(CreateProjectViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
Project project = Mapper.Map<CreateProjectViewModel, Project>(model);
// pass the project entity to your service layer
_projectService.Create(project);
return RedirectToAction("Index");
}
ou se você estiver atualizando uma entidade que você pode querer primeiro buscar a entidade existente que pretende actualizar a partir do serviço:
[HttpPost]
public ActionResult Update(CreateProjectViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
Project project = _projectService.GetById(model.Id);
Mapper.Map<CreateProjectViewModel, Project>(model, project);
// pass the project entity to your service layer
_projectService.Update(project);
return RedirectToAction("Index");
}
Outras dicas
A única maneira que eu já vi esse feito até agora é criar manualmente um grupo de classes de modelo de transformação, por exemplo:
public interface ITransformer<out To, in From>
where To : class
{
To Transform(From instance);
}
public class SomeDataToSomeViewModelTransformer : ITransformer<SomeViewModel, SomeDataModel>
{
public SomeViewModel Transform(SomeDataModel instance)
{
return new SomeViewModel
{
InvitationId = instance.Id,
Email = instance.EmailAddress,
GroupId = instance.Group.Id
};
}
}
E outra implementação Transformer para voltar para o outro lado (ViewModel -> DataModel
).
E ter o controlador sabe para chamar o transformador correto.
I +1 sua pergunta, porque eu adoraria ver uma maneira limpa agradável de fazer isso também, sem ter que escrever manualmente um monte de código para mapear modelos.
Se a sua camada de serviço é exclusivamente dedicado a apoiar a sua aplicação MVC e há outros clientes que você poderia considerar usando os objetos passaram e de sua camada de serviço como parte de seus viewmodels. Isto evitaria a necessidade de AUTOMAP as chamadas de entrada como você estaria enviando nos objetos reais necessários a partir do controlador.
Você também pode considerar não ter os serviços retornar objetos de domínio, isso significaria que o mapeamento automático deve ser invocado com os métodos do serviço, em vez das ações do controlador.