Help improve (refactor) my code. Automapper - EF - asp.net mvc-3

I have these 4 models - 2 domain models and 2 DTO

public class Project { public int ID { get; set; } public string Name { get; set; } public virtual ICollection<Task> Tasks { get; set; } } public class Task { public int ID { get; set; } public virtual int ProjectID { get; set; } public string Name { get; set; } public virtual Project Project { get; set; } } public class ProjectDTO { [Required] public string Name { get; set; } public List<TaskDTO> Tasks { get; set; } } public class TaskDTO { [Required] public string Name { get; set; } public int ID { get; set; } public bool MarkRemove { get; set; } } 

Here is my automapper configuration

 Mapper.CreateMap<Project, ProjectDTO>(); Mapper.CreateMap<ProjectDTO, Project>().ForMember(p =>p.ID, opt=>opt.Ignore()).ForMember(p=>p.Tasks, opt=>opt.Ignore()); Mapper.CreateMap<Task, TaskDTO>(); Mapper.CreateMap<TaskDTO, Task>().ForMember(task=>task.ProjectID, opt=>opt.Ignore()).ForMember(task=>task.Project, opt=>opt.Ignore()); 

Here is my HttpPost Edit action

 [HttpPost] public ActionResult Edit(int id, ProjectDTO p) { if (ModelState.IsValid) { var dbProject = db.Projects.Where(pr => pr.ID == id).Single(); Mapper.Map(p, dbProject); foreach (var task in p.Tasks) { Task dbTask; try { dbTask = dbProject.Tasks.Where(t => t.ID == task.ID).Single(); } catch { dbTask = new Task(); Mapper.Map(task, dbTask); dbProject.Tasks.Add(dbTask); } if (task.MarkRemove) { db.Tasks.Remove(dbTask); } else { Mapper.Map(task, dbTask); } } db.Entry(dbProject).State = EntityState.Modified; db.SaveChanges(); TempData["Success"] = "Modelo Valido"; return RedirectToAction("Index"); } return View(p); } 

I am not quite happy with this, but I donโ€™t think there is a cleaner approach to handling this somewhat complex scenario ....

now that it is working, I would like to at least reorganize it to use the repository template or something in the sense that the controllerโ€™s action was not confused. Ultimately, this will be production code: s

can anyone give me some advice on how to reorganize this? please, help.

0
source share
2 answers

I would use a service level, for example:

 public interface IProjectsService { void RemoveTasks(int projectId, IEnumerable<int> taskIdsToRemove); } 

and then the controller will depend on this level of service:

 public class ProjectsController : Controller { private readonly IProjectsService _service; public ProjectsController(IProjectsService service) { _service = service; } public ActionResult Edit(int id) { // TODO: Add methods to your service layer // allowing to retrieve projects, then map // the resulting project into a view model throw new NotImplementedException(); } [HttpPost] public ActionResult Edit(int id, ProjectDTO p) { if (!ModelState.IsValid) { return View(p); } var taskIdsToRemove = p.Tasks.Where(x => x.MarkRemove).Select(x => x.ID); _service.RemoveTasks(id, taskIdsToRemove); TempData["Success"] = "Modelo Valido"; return RedirectToAction("Index"); } } 

Thus, the controller logic is more loosely related to the way we access the data. This is an implementation detail that the controller should never worry about.

As an additional improvement to the RemoveTasks method, you can force it to return a boolean, indicating that the operation was successful or failed along with the error message, so that the Edit action can display the view and show an error if something goes wrong.

Now, for this level of service, the RemoveTasks method is a business operation that can be built on several CRUD operations with some repository. Thus, this level of service will depend on the repository. Only this repository should know about EF or what you use to access data.

So basically basically every time I see someone asking a question about ASP.NET MVC and EF at the same time, for me these are two completely different questions. ASP.NET MVC does not need to know anything about EF. EF must be hidden behind storage abstraction.

+1
source

I understand that this question has been inactive for some time, but I had the same problem, and although I would publish my solution for everyone who is struggling with a nested model that is not saved during editing.

  [HttpPost] public ActionResult Edit(ProjectDTO p) { if (ModelState.IsValid) { // *****MODIFIED CODE HERE******** for (int i = 0; i < p.Tasks.Count; i++) { db.Entry(p.Tasks[i]).State = EntityState.Modified; } // ************************************* db.Entry(dbProject).State = EntityState.Modified; db.SaveChanges(); TempData["Success"] = "Modelo Valido"; return RedirectToAction("Index"); } return View(p); } 

Basically, you want to set the state of each nested model to the Modified as well as the root model.

+1
source

All Articles