MVC - Controller per Servizio Communication Layer
-
27-10-2019 - |
Domanda
Nel mio ASP.net MVC app Sto usando un livello di servizio e repository per mantenere il mio controller sottile. A dettagli tipici letti Visualizza solo assomiglia a questo:
public ActionResult Details(int id)
{
var project = _projectService.GetById(id);
return View(Mapper.Map<Project, ProjectDetails>(project));
}
Servizio di livello:
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);
}
}
Passando dal livello di servizio al modello vista è abbastanza facile a causa della automapper, che può appiattire le cose abbastanza facilmente. Spostare l'altro direttamente, dal modello al fine di passare nel mio livello di servizio è dove faccio fatica a trovare una buona soluzione.
In una situazione come un'azione di Crea, che cosa è un approccio buono per questo?
[HttpPost]
public ActionResult Create(CreateProjectViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
// TODO
return RedirectToAction("Index");
}
Sono abbastanza sicuro che il livello di servizio non deve sapere nulla di modelli vista, ma ho anche non credo che automapper funziona bene in questo scenario sia, dato che non è bravo a prendere un modello piatto e trasformandolo in un oggetto complesso.
Che cosa dovrebbe il mio sguardo di controllo, come per comunicare con il livello di servizio? Voglio mantenere il codice nel controller il più leggero possibile.
Soluzione
Si potrebbe definire una mappatura bidirezionale e poi il contrario:
[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");
}
o se si sta aggiornando un soggetto si potrebbe voler prima per andare a prendere l'entità esistente che si desidera aggiornare dal servizio:
[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");
}
Altri suggerimenti
L'unico modo che ho visto fare finora è quello di creare manualmente un gruppo di classi modello di trasformazione, per esempio:
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 un'altra implementazione del trasformatore di tornare indietro l'altro modo (ViewModel -> DataModel
).
E avendo il controller sa di chiamare il corretto trasformatore.
I +1 tua domanda perché mi piacerebbe vedere un bel modo pulito per fare anche questo, senza dover scrivere manualmente un mucchio di codice per mappare modelli.
If your service layer is solely dedicated to support your MVC application and no other clients you could consider using the objects passed through and from your service layer as part of your viewmodels. This would obviate the need to automap the inbound calls as you'd be sending in the actual objects required from the controller.
You could also consider not having the services return domain objects, this would mean that the automapping should be invoked with the service methods rather than the controller actions.