I have an application for storing information about consultants in a database. The model is an Entity Framework model, and the database tables are the “Consultant” with a one-to-many relationship to a number of other tables (WorkExperiences, Programs, CompetenceAreas, etc.). Now that I want to create a new Consultant object in the view, I would just like to pass the Consultant object as a model for the view. But for me it was suggested ( A collection of complex child objects in an Asp.Net MVC 3 application? ) That I should not do this, but use ViewModels instead. Secondly, and perhaps for this very reason, I get the error message: "EntityCollection is already initialized" when I try to publish the "Consultant" object, if I use it as a model in the view, and the cause of the error seems to be is a collection of objects such as WorkExperiences.
So my first question is why am I getting this error.
But more importantly, if I have to use ViewModel, how would I do it right? Because I actually tried and earned something. But ... the code is terrible. Can someone please tell me what should I do to get this to work more cleanly?
Let me show you what I have (which works again, but this is a nightmare code):
GET Create Method:
public ActionResult Create() { Consultant consultant = new Consultant(); ConsultantViewModel vm = GetViewModel(consultant); return View(vm); }
A helper method for creating a "ViewModel" (if that's really what should look like a ViewModel):
private ConsultantViewModel GetViewModel(Consultant consultant) { ConsultantViewModel vm = new ConsultantViewModel(); vm.FirstName = consultant.FirstName; vm.LastName = consultant.LastName; vm.UserName = consultant.UserName; vm.Description = consultant.Description; vm.Programs = consultant.Programs.ToList(); vm.Languages = consultant.Languages.ToList(); vm.Educations = consultant.Educations.ToList(); vm.CompetenceAreas = consultant.CompetenceAreas.ToList(); vm.WorkExperiences = consultant.WorkExperiences.ToList(); return vm; }
POST Create Method:
[HttpPost] [ValidateInput(false)] //To allow HTML in description box public ActionResult Create(ConsultantViewModel vm, FormCollection collection) { try { Consultant consultant = CreateConsultant(vm); _repository.AddConsultant(consultant); _repository.Save(); return RedirectToAction("Index"); } catch { return View(); } }
A helper method for creating a consultant object (this is especially terrible when I have to verify that collections are not null if the user decides not to add anything to these lists ...):
private Consultant CreateConsultant(ConsultantViewModel vm) { Consultant consultant = new Consultant(); consultant.Description = vm.Description; consultant.FirstName = vm.FirstName; consultant.LastName = vm.LastName; consultant.UserName = vm.UserName; if (vm.Programs != null) foreach (var program in vm.Programs) consultant.Programs.Add(program); if (vm.Languages != null) foreach (var language in vm.Languages) consultant.Languages.Add(language); if (vm.Educations != null) foreach (var education in vm.Educations) consultant.Educations.Add(education); if (vm.WorkExperiences != null) foreach (var workExperience in vm.WorkExperiences) consultant.WorkExperiences.Add(workExperience); if (vm.CompetenceAreas != null) foreach (var competenceArea in vm.CompetenceAreas) consultant.CompetenceAreas.Add(competenceArea); return consultant; }
So, it works again, but is nowhere near as clean as if I could use the consultant object directly (if not for the fact that the "EntityCollection is already initialized" "error" ...). How do I do this?