Doing database access in ViewModel is generally considered a bad practice. However, when you have a <select>
in your view, you need to populate it using the ViewBag or the ViewModel (I prefer the ViewModel). This leads to the following code:
public class NewUserViewModel
{
public string Username {get;set;}
public IEnumerable<SelectListItem> Cities {get;set;}
public User User // Helper to retrieve user
{
get { return new User() { Username = this.Username; }}
set { this.Username = value.Username; }
}
}
We now have to populate the Cities twice:
public ActionResult New()
{
var vm = new NewUserViewModel();
vm.Cities = addressRepository.GetAllCities().Select(c => new SelectListItem() { Text = c.Name, Value = c.Id });
return View(vm);
}
[HttpPost]
public ActionResult Create(NewUserViewModel vm)
{
if(ModelState.IsValid)
{
userRepository.Add(vm.User);
return RedirectToAction("Index");
}
else
{
vm.Cities = addressRepository.GetAllCities().Select(c => new SelectListItem() { Text = c.Name, Value = c.Id }); // Copy this line
return View(vm);
}
}
I came up with an approach to solve this problem:
public NewUserViewModel(AddressRepository addressRepository) {
this.Cities = addressRepository.GetAllCities().Select(c => new SelectListItem() { Text = c.Name, Value = c.Id });
}
I overrode the DefaultModelBinder to inject the Repository into the ViewModel.
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
return DependencyResolver.Current.GetService(modelType);
}
Now my controller is free:
public ActionResult New()
{
// Is this bad too?
return View(DependencyResolver.Current.GetService<NewUserViewModel>());
}
public ActionResult Create(NewUserViewModel vm)
{
if(ModelState.IsValid)
{
userRepository.Add(vm.User);
return RedirectToAction("Index");
}
else
{
return View(vm);
}
}
If I think about it, having all the cities listed is ViewModel's job, not the controller's. This has one immediate downside: it fetches the Cities
even when the ModelState.IsValid
. However, that is not my concern. Is this bad practice? I won't do any more database access than this.