谁有责任加载数据?

时间:2009-10-20 09:20:46

标签: asp.net-mvc model-view-controller

在MVC模型中,加载视图模型的责任在哪里?

Controller应该加载数据吗? 视图模型本身是否应加载数据ala:    MyViewModel viewModel = MyViewModel.Create(someValue); 如果服务层加载它ala:    MyViewModel viewModel = MembershipService.Instance.Load(someValue);

5 个答案:

答案 0 :(得分:2)

控制器是粘合模型和视图的粘合剂。您希望模型类和视图在应用程序的其他层上具有尽可能少的依赖关系。因此,控制器应始终为视图加载数据,无论数据来自何处,或者在模型中使用哪种设计模式进行检索。

您的模型层中的数据加载操作可能有一堆抽象层,但是控制器应该调用某些方法或方法,这些方法或方法在某些时候在调用链中调用到任何持久性数据存储区。使用并获取视图所需的数据。

控制器应该提供视图所需的所有对象,因为这确实是其关键职责之一。这是否意味着使用适当的模型对象从数据库中获取数据,或初始化“视图模型”类以包装视图显示所需的所有对象和属性,这无关紧要。

就个人而言,我一直将Linq-to-SQL与ASP.Net MVC结合使用,并且使用从数据上下文中获取必要对象的存储库类取得了巨大成功。为了允许我的控制器进行单元测试,我使用依赖注入框架(在我的例子中是StructureMap)让ASP.Net MVC为我的控制器提供我的存储库接口的默认实例。

答案 1 :(得分:2)

请参阅这个非常干净的技术示例: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/29/how-we-do-mvc-view-models.aspx

或者您可以手动执行此操作:有关示例,请参阅“ASP.NET MVC操作”一书或CodeCampServer来源。基本上你注入IViewModelMapper {public ViewModel Map(data);到控制器。巧妙的是,它使您的IoC自动将服务和存储库传递给ViewModel映射器。然而,这可以让控制器在使用映射器接口时变得臃肿,所以就像Jimmy Bogard的技术,即使没有AutoMapper,但是使用动作过滤器而不是选择IViewModelMapper,会更好。

如果你不能这样做,那么我建议像Mathias建议的那样坚持使用ViewModel处理映射。

更新:这是一个类似于Automaticpper的配置示例,其中包含CodeCampServer方式。不确定它是否可以正常工作(或者完全有用),只是演示。

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

答案 2 :(得分:1)

我喜欢让ViewModel加载数据。

可以从所有控制器访问ViewModel。这样就可以避免代码重复。

请参阅此示例ASP.NET MVC - Job of Controllers

答案 3 :(得分:0)

控制器永远不应该加载数据。应该在模型中或在数据层中。

我的偏好是在一个数据层,所以我可以从模型中分离它,我认为只存储/表示给予控制器然后视图的数据。

我实现了数据检索的存储库模式

答案 4 :(得分:0)

我用这种方式分层:

视图 - &GT;控制器 - &GT;服务 - &GT;持久性

请求从前到后流动;回复从后到前。

持久层担心数据库;它使模型数据可用。它对任何其他层都一无所知。

服务层的方法映射到用例。它知道持久层,工作单元和事务。它验证其输入,获取数据库连接,使它们可用于持久层,清理资源,并使用模型对象来完成用例。如果需要,它可以作为Web服务或EJB公开。

控制器和视图一起使用。它知道服务层。它将请求映射到服务,绑定和验证传入的请求参数,将它们传递给服务层,并将响应路由到适当的视图。

该视图仅担心显示控制器为其提供的数据。它知道控制器。

所以这是处理数据库的服务。控制器和视图协作显示信息,仅此而已。