views:

174

answers:

5

In the MVC model, where does the responsibility of loading the view model lie?

Should the Controller load the data? Should the View Model itself load the data ala: MyViewModel viewModel = MyViewModel.Create(someValue); Should a Service Layer load it ala: MyViewModel viewModel = MembershipService.Instance.Load(someValue);

A: 

the controller should never load data. that should be in either the model or in a data layer.

my preference is in a data layer so i can seperate it from the model which i think isa there only to store/represent data that is given to the controller and then the view.

i implement the repository pattern for data retrieval

griegs
We are using a repository for loading data from the database, and a service that acts as the API for the system, utilizing the repository. But it seems kind of odd to put view model logic in the API layer as it's very specific to the view. Maybe there needs to be another layer on top to translate between API <-> View Model?Oh so many layers...
Micael
the whole point i think is seperation of concerns. keeping the model away from the data collection means that you can replace either and keep the other without major changes. i'm sure you know this and yeah it may seem odd in your case but keeping it seperate is the way to go. i have had to make changes in the past and it was so easy to switch from one database to the next w/out touching the model
griegs
So in our case it would be:View -> Controller -> Service -> API -> PersistenceI can see that we seem to have an extra layer - the API, that duffymo doesn't have - this is where I would place any business logic - i.e. send a notification email when an order is recieved. I would think this should be seperate from the Service that knows about the View Model since the API could be used from other types of applications/services - would you agree?
Micael
When you say "the controller should never load data", do you really mean to say "the controller should never load data directly"? The second statement, I would agree with almost 100% of the time, but the controller will certainly need to load data for the view, but the actual code that retrieves that data from the persistence layer should be abstracted in to some kind of service/data access/repository object.
Justin Holzer
A: 

I layer things this way:

view->controller->service->persistence

Requests flow from front to back; responses go from back to front.

The persistence layer worries about the database; it makes model data available. It knows nothing about any of the other layers.

The service layer's methods map to use cases. It knows about the persistence layer, units of work and transactions. It validates its inputs, acquires database connections, makes them available to the persistence layer, cleans up resources, and works with model objects to fulfill the use cases. It can be exposed as a web service or EJB if needed.

The controller and view go together. It knows about the service layer. It maps requests to services, binds and validates incoming request parameters, passes them to the service layer, and routes responses to the appropriate view.

The view only worries about displaying the data that the controller provides for it. It knows about the controller.

So it's the service that deals with the database. Controller and view collaborate to display info, nothing more.

duffymo
+1  A: 

I like to have the ViewModel load the data.

ViewModels are accessible from all Controllers. That way its possible to avoid code dupplication.

See this sample http://stackoverflow.com/questions/1240483/asp-net-mvc-job-of-controllers/1240766#1240766

Malcolm Frexner
I have to respectfully disagree with this approach. Why should the "View Model" do anything other than be a wrapper for the data that is consumed by the view? What is the benefit of having your controller call the `FillPackageList()` method in your view model rather than just calling the service method directly from the conotrlller? In my opinion, this is an unnecessary abstraction that adds additional complexity. The controller should load all the data, and then populate the view model properties as needed. Just call the service method from the conotroller.
Justin Holzer
If you can fill the ViewModel by just one call to the service layer I guess thats realy the way to go. For complex ViewModels this does not work well. There is often one place where some kind of agregation takes place. Lets say this page on SO: You would need a very special method in the Service that loads the objects for one ViewModel - RelatedPosts, Question, Answers, Posts, User. This special method looks not right in a Service.I agree: It doesnt look right. It just worked out very well so far.
Malcolm Frexner
I understand what you are saying. It really all boils down to preference I guess. "Good design" is certainly subjective. In the case you speak of, personally, I would prefer to have the controller call separate methods in my service/repository class for retrieving each of the data items. For instance, instead of calling `viewModel.FillAnswers()`, `viewModel.FillPosts()`, etc, I would just as soon call the service methods directly from the controller.
Justin Holzer
I think Mathias has a good point, with the fact that ViewModels almost always draws on data from multiple distinct areas of an application.If we need to create a Service to provide specific data for each page/view, then a factory method on the ViewModel would seem to be a simpler design?
Micael
I think it's really just personal preference. It also depends which perspective you choose to look at things from. To go back to Mathias' example of creating service methods for loading the data for this page on SO, you might need to load questions, answers, etc. If you look at all of this data as being related to a Question, then you might have a QuestionRepository object with methods like `GetQuestionById()` and `GetAnswersWithComments(int questionId)`. In other words, the `QuestionRepostiory` object would be able to fetch data for the question and any related data as well.
Justin Holzer
+1  A: 

The controller is the glue that binds the model and view together. You want both your model classes and views to have as few dependencies as possible on the other layers of your app. Therefore, the controller should always load data for the view, regardless of where the data comes from, or what design patterns you use in your model for retrieving it.

You may have a bunch of layers of abstraction for your data loading operations within your model layer, but the controller should be calling some method, or methods, which at some point down the call chain, goes to whatever persistent datastore you are using and gets the data needed for your view.

The controller should be providing all of the objects that the view needs, as this is really one of its key responsibilities. Whether that means using the appropriate model object to grab the data from a database, or initializing a "view model" class to wrap all the objects and properties needed for the view to display, it doesn't matter.

Personally, I have always used Linq-to-SQL in conjunction with ASP.Net MVC, and have had great success using repository classes that grab the necessary objects from the data context. To allow my controllers to be unit tested, I use a dependency injection framework (StructureMap in my case) to have ASP.Net MVC provide my controller with a default instance of my repository interface.

Justin Holzer
+1  A: 

See this example of the really clean technique: http://www.lostechies.com/blogs/jimmy%5Fbogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx

Alternatively you can do it manually: see "ASP.NET MVC In Action" book or CodeCampServer sources for examples. Basically you inject IViewModelMapper { public ViewModel Map(data); } to the controller. The neat thing is that it makes your IoC automatically pass services and repositories to your ViewModel mapper. However this can really make controllers be bloated with mapper interfaces so something like Jimmy Bogard's technique, even without AutoMapper, but with action filters than do pick IViewModelMapper, would be better.

If you can't do this, then I'd suggest stick with ViewModel handling mapping as Mathias suggested.

UPDATE: here's an example of automapper-like configuration with bits of CodeCampServer way. Not sure if it will work as is (or useful at all), just a demonstration.

public abstract class ViewModelMapper<Source, ViewModel> where Source: class, ViewModel: IViewModel
{
  public abstract ViewModel Map(Source source);
}

public class ProductDetailsViewModel
{
  public ProductViewModel Product { get; set; }
  punlic IList<Language> AvailableProductLanguages { get; set; }
}

public class ProductDetailsViewMapper: ViewModelMapper<Product, ProductDetailsViewModel>
{
  private ILanguageRepository languages;
  public ProductDetailsViewMapper(ILanguageRepository languages)
  {
     this.languages = languages;
  }
  public override ProductDetailsViewModel Map(Product source)
  {
     var vm = new ProductDetailsViewModel();
     AutoMapper.Map<Product, ProductDetailsViewModel>(product, vm);
     vm.AvailableProductLanguages = languages.GetAppropriateFor(product);
  }
}

public class ViewModelMapperActionFilter: ActionFilter
{
  Type mapperType;
  public ViewModelMapperActionFilter()
  {
  }
  public ViewModelMapperActionFilter(Type mapperType)
  {
  }
  public void OnActionExecuted(ControllerContext context)
  {
    var model = context.Result.ViewData.Model;
    var mapperType = this.MapperType ?? this.GetMapperTypeFromContext(context);
    // this is where magic happens - IoC grabs all required dependencies
    var mapper = ServiceLocator.GetInstance(mapperType);
    var method = mapperType.GetMethod("Map");
    Check.Assert(method.GetArguments()[0].ArgumentType == model.GetType());
    context.Result.ViewData.Model = method.Invoke(mapper, new[]{model});
  }
}

public class ProductsController: Controller
{
  [ViewModelMapper(typeof(ProductDetailsViewMapper))]
  // alternatively [ViewModelMapper()] will auto-pick mapper name by controller/action
  public ActionResult Details(EntityViewModel<Product> product)
  {
    // EntityViewModel is a special type, see 
    // http://stackoverflow.com/questions/1453641/my-custom-asp-net-mvc-entity-binding-is-it-a-good-solution
    return View(product.Instance); 
  }
}

//Global.asax.cs:
IoC.Register(AllTypes.DerivedFrom(typeof(ViewModelMapper<>)));
queen3
Good link - however as I read it in their specific case their ViewModel contains a condensed version of a domain object. It doesn't aggregate data from various services.I think I'm beginning to feel that for now Mathias solution may be the best. :)
Micael
That's why I suggested to combine this and IViewModelMapper together. I don't have a ready solution; but I think you can still use AutoMapper and do things like CreateMap<ViewModel, Model>().For(vm => vm.Languages, ServiceLocator.Get<IRepository<Language>>().GetAll()), i.e. even with AutoMapper it's still possible to aggregate from different sources. The real thing is to avoid duplicated work; constuctor parameters, etc; the action filter to tell about mapping is a really good idea in my opinion, since ViewModel mapping is usuall per-action.
queen3
Thanks for updating with the source code - it really helped to visualize it.
Micael
My example is too simple... go for nested view models and it becomes tricky. That's why I still hesitate to use AutoMapper even though it's so easier and promising. Hope you'll be able to utilize it.
queen3