谁填充了ASP MVC 5中的ViewModel

时间:2014-10-15 20:11:54

标签: c# asp.net-mvc entity-framework viewmodel

在ASP MVC 5体系结构(C#,EF)中填充值的责任是什么?如果我们有PurchaseRecordsViewModel , PurchaseRecords Domain Model , PurchaseController

  • 填充数据的代码(时间,成本等)是否与viewmodel一致,就在它自己的viewmodel中进入PurchaseRecordsViewModel

  • 或者,代码是否采用PurchaseController

  • 的Action方法

4 个答案:

答案 0 :(得分:11)

查看模型通常只是属性的愚蠢集合。填充视图模型通常位于服务层内部,如果没有,则采用操作方法。

以这种方式思考角色。

  • 域模型是到数据库表的直接映射。
  • 视图模型是显示视图所需的属性的集合。
  • 服务层获取/使用一个或多个域模型并填充视图模型。
  • 服务层也可以采用视图模型并创建/更新一个或多个域模型
  • 控制器动作方法是两者之间的粘合剂。它调用服务层来获取(GET)视图模型并将其传递给视图。这些操作方法还采用(POST)视图模型并将其传递给服务层,以执行任何需要对其执行的操作。

通常会问的另一个问题是为什么我不能将域模型用于视图?您可以,但通常会遇到类似这样的问题,需要来自多个域模型的数据,不需要域模型中的所有属性,最后,您现在必须担心在您的域模型上更新属性不打算。

答案 1 :(得分:8)

根据汤米的回答,这里有一些代码可以与他的描述一致。

//Controller

public ActionResult Index()
{
  List<OrderViewModel>() model = new List<OrderViewModel>();  
  model = new ServiceClass().GetOrders();

  return View(model);
}

//here is your Service Class, this layer transfers the Domain Model into your ViewModel
public List<OrderViewModel> GetOrders()
{
   List<OrderDomain> model = new List<OrderDomain>();

   model = new DataAccess().GetOrders();

   List<OrderViewModel> viewModel = new List<OrderViewModel>();

   foreach (var order in model)
   {
        OrderViewModel vm = new OrderViewModel();
        vm.OrderId = order.OrderId;
        vm.OrderName = order.OrderName;

        viewModel.Add(vm);
   }      

    return viewModel;        
}

//some DataAccess class, this class is used for database access

Public List<OrderDomain> GetOrders()
{
     List<OrderDomain> model = new List<OrderDomain>();

      using (var context = new MyEntities())
      {
          model = (from x in context.Order
                   select new OrderDomain
                   {
                     OrderId = x.OrderId,
                     OrderName = x.OrderName
                   }).ToList();                     
      }
   return model;
}

编辑: 这似乎是一个温和的流行答案,所以我想提一下我不再遵循这种模式。相反,我一直在使用mediatr和垂直切片架构。

答案 2 :(得分:7)

理想情况下,PurchaseRecordViewModel应该通过获取PurchaseRecordsDomainModel来填充自己。它应该包含属性的简单映射,以及可能在视图中使用的输出格式。

<强> PurchaseRecordsViewModel

public class PurchaseRecordsViewModel
{
   public IEnumerable<PurchaseRecordViewModel> PurchaseRecords {get;set;}
}

<强> PurchaseRecordViewModel

 public class PurchaseRecordViewModel
 {
    public DateTime Date {get;set;}
    public decimal Cost {get;set;}
    // .... some other properties
    public PurchaseRecordsViewModel(PurchaseRecordsDomainModel domainModel)
    {
       Date = domainModel.Date;
       Cost = domainModel.Cost;
       // .... some other property mappings
    }
 }

action PurchaseController方法应该做什么,正在协调从PurchaseRecordsDomainModel获取PurchaseRecordsViewModel,创建PurchaseRecordsDomainModel并将其传递给View的过程ActionEF方法本身不应包含任何处理从数据库连接和检索数据的代码(在您查询abstractions上下文的情况下)或任何业务逻辑。你应该尝试使用松散耦合的模块,通过maintainable相互交谈,这样你就可以确保你的应用程序是extensibletestableEF entities

此外,尝试在系统的各个层之间绘制清晰的分隔。例如,将Domain Model Entites设为business logic layer不是一个好主意。你不希望你的data access layer依赖EF,以这种方式思考,如果在将来的某个时候,你正在远离ORM和使用其他business logic layer甚至其他技术来存储和查询数据。您不想仅仅因为您正在更改data access layer而更改view。所以在你的案例中从单词转到代码。

考虑到您已经拥有view modelPurchaseRecordsService,我会在domain layer中创建Repositories课程(请注意,根据您的情况,您可能不会使用public class PurchaseRecordsService { private readonly IPurchaseRecordsRepository _purchaseRecordsRepository; public PurchaseRecordsService(IPurchaseRecordsRepository purchaseRecordsRepository) { if(purchaseRecordsRepository == null) { throw new ArgumentNullException("purchaseRecordsRepository"); } _purchaseRecordsRepository = purchaseRecordsRepository; } public IEnumerable<PurchaseRecordsDomainModel> GetPurchaseRecords() { // trivial case, real code can be more complex return _purchaseRecordsRepository.GetPurchaseRecords(); } } 但是,其他一些技巧,这个例子主要是为了说明我的观点)

domain layer

然后在IPurchaseRecordsRepository中,您可以定义public interface IPurchaseRecordsRepository { IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords(); }

PurchaseRecordsService

我们的想法是,IPurchaseRecordsRepository需要一种与数据库通信的方式,因此无论谁使用它,都必须提供data access layer的实现。下一步是转到我们的IPurchaseRecordsRepository并创建实现类public class EfPurchaseRecordsRepository: IPurchaseRecordsRepository { private readonly EfObjectContext _objectContext; public EfPurchaseRecordsRepository(string connectionString) { _objectContext = new EfObjectContext(connectionString); } public IEnumerable<PurchaseRecordsDomainModel > GetPurchaseRecords() { var purchaseRecords = (from p in _objectContext.PurchaseRecords .... select p).AsEnumerable(); return purchaseRecords .Select(p => p.ConvertToDomainPurchaseRecord()); } }

Action

最后一部分 - 我们需要在PurchaseController

中定义public class PurchaseController: Controller { private readonly IPurchaseRecordsRepository _repository; public PurchaseController(IPurchaseRecordsRepository repository) { if(repository == null) { throw new ArgumentNullException("repository"); } _repository = repository; } public ActionResult Index() { var purchaseRecordsService = new PurchaseRecordsService(_repository); var purchaseRecordsViewModel = new PurchaseRecordsViewModel(); var purchaseRecords = purchaseRecordsService.GetPurchaseRecords(); foreach(var purchaseRecord in purchaseRecords) { var purchaseRecordViewModel = new PurchaseRecordViewModel(purchaseRecord); purchaseRecordsViewModel.PurchaseRecords.Add(purchaseRecordViewModel); } return View(purchaseRecordsViewModel); } }
Presentation

总结一下,我们所拥有的是松散耦合的代码,我们的Data AccessDomain图层彼此之间并不了解,而且它们仅依赖于MVC图层。如果需要,您可以将WPF前端替换为EF,例如,从{{1}}移至另一项技术,您的代码可以测试。

答案 3 :(得分:3)

理想情况下,您的视图模型应该不知道您的域模型,所以我要说您将人口逻辑放在控制器中,可能包含在某种映射/填充实用程序类中。

但请记住,当涉及到将某些逻辑置于何处的问题时,个人偏好会有很长的路要走。