Reputation: 5929
Following on from this question
MVC3 - Should I design my Model to be tightly coupled to my View?
about how it is recommended to use a view model for your views and have the Controller populate the view model, I have been trying out Ninject.MVC and used some examples for the repository pattern to inject the required repository for a controller.
Like this
public RecipesController(IRepository<Member> memberRepository, IRepository<Course> courseRepository, IRepository<Cuisine> cuisineRepository, IRepository<Recipe> recipeRepository) {
this.memberRepository = memberRepository;
this.courseRepository = courseRepository;
this.cuisineRepository = cuisineRepository;
this.recipeRepository = recipeRepository;
}
Then I used MVC Scaffolding to see what the actions looked like
public ActionResult Create() {
ViewBag.PossibleCuisines = cuisineRepository.All;
ViewBag.PossibleMembers = memberRepository.All;
ViewBag.PossibleCourses = courseRepository.All;
return View();
}
[HttpPost]
public ActionResult Create(Recipe recipe) {
if (ModelState.IsValid) {
recipeRepository.InsertOrUpdate(recipe);
recipeRepository.Save();
return RedirectToAction("Index");
} else {
ViewBag.PossibleMembers = memberRepository.All;
ViewBag.PossibleCourses = courseRepository.All;
ViewBag.PossibleCuisines = cuisineRepository.All;
return View();
}
}
I am having a hard time understanding how to approach the controller actions by using a view model.
Say I have a RecipeViewModel like this:
public class RecipeViewModel {
public Recipe Recipe { get; set; }
public SelectList AuthorList { get; set; }
public SelectList CourseList { get; set; }
public SelectList CuisineList { get; set; }
public RecipeViewModel(Recipe recipe) {
Recipe = recipe;
}
}
and this is the model my view would use. I presume the Create() GET action would first create this view model and would have to create a new Recipe object to pass to the ViewModel's constructor? and the select lists can be populated by using the relevant repository such as cuisineRepository.All (but this seems like it would be duplicated in each action) and then the view model is passed to the view.
How though does ModelState.IsValid in the Create() POST action work with regards to this view model?
By doing this my controller now expects a RecipeViewModel object which itself needs a Recipe object.
Should it use interfaces for these too and have Ninject handle the rest? is this advisable?
Upvotes: 1
Views: 2716
Reputation: 7443
First of all the RecipeViewModel shouldn't contain the Recipe object but in containing members such as:
public class RecipeViewModel {
public RecipeViewModel(IRepository<Course> courseRepository, IRepository<Cuisine> cuisineRepository, IRepository<Recipe> recipeRepository){
this.courseRepository = courseRepository;
this.cuisineRepository = cuisineRepository;
this.recipeRepository = recipeRepository;
}
public string RecipeName { get; set; }
public IList<IngredientsViewModel> Ingredients { get; set;}
public SelectList AuthorList { get; set; }
public SelectList CourseList { get; set; }
public SelectList CuisineList { get; set; }
public static RecipeViewModel Build()
{
//Build up Select Lists here and return View model.
}
}
Then Validation attributes go on the ViewModel to make ModelState.IsValid work:
public class RecipeViewModel {
[Required]
public string RecipeName { get; set; }
public IList<IngredientsViewModel> Ingredients { get; set;}
public SelectList AuthorList { get; set; }
public SelectList CourseList { get; set; }
public SelectList CuisineList { get; set; }
}
Personally I would then refactor your controller to decouple the data layer from the view.
public RecipesController(IRecipeService recipeService) {
this.recipeService = recipeService;
}
Then the rest of the controller looks like:
public ActionResult Create() {
var recipeViewModel = new RecipeViewModel();
recipeViewModel.Build():
ViewBag.PossibleCuisines = recipeViewModel.CuisineList;
ViewBag.PossibleMembers = recipeViewModel.AuthorsList;
ViewBag.PossibleCourses = recipeViewModel.CourceList;
return View();
}
[HttpPost]
public ActionResult Create(RecipeViewModel recipe) {
if (ModelState.IsValid) {
_recipeService.Save(recipe)
return RedirectToAction("Index");
} else {
return View();
}
}
The IRecipeService then does the mapping from the view model to the domain model (in this case RecipeViewModel to Recipe) and then persist the domain model.
Upvotes: 3