Who has the responsibilty of loading data?

2019-04-11 23:45发布

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);

5条回答
你好瞎i
2楼-- · 2019-04-11 23:57

See this example of the really clean technique: http://www.lostechies.com/blogs/jimmy_bogard/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<>)));
查看更多
3楼-- · 2019-04-11 23:59

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

查看更多
Animai°情兽
4楼-- · 2019-04-12 00:01

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 ASP.NET MVC - Job of Controllers

查看更多
ら.Afraid
5楼-- · 2019-04-12 00:04

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.

查看更多
我想做一个坏孩纸
6楼-- · 2019-04-12 00:11

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.

查看更多
登录 后发表回答