谁在ASP MVC 5中填充ViewModel

本文关键字:填充 ViewModel ASP MVC 谁在 | 更新日期: 2023-09-27 18:26:36

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

  • 填充数据(时间、成本等)的代码是否进入视图模型,视图模型是否进入PurchaseRecordsViewModel

  • 或者,代码是否进入PurchaseController 的Action方法

谁在ASP MVC 5中填充ViewModel

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

这样想角色。

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

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

扩展Tommy的回答,下面是一些代码来配合他的描述。

//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;
}

编辑:这似乎是一个稍微流行的答案,所以我想说的是,我不再遵循这种模式。相反,我一直在使用中介器和垂直切片架构。

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

购买记录ViewModel

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

购买RecordViewModel

 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
    }
 }

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

此外,试着在系统的各个层之间画出清晰的分隔。例如,将EF entities作为Domain Model Entites不是一个好主意。你不希望你的business logic layer依赖于data access layer,这样想吧,如果在未来的某个时候,你正在远离EF,使用其他ORM甚至其他技术来存储和查询数据,会怎么样。您不想仅仅因为要更改data access layer而更改business logic layer。因此,在您的情况下,从单词到代码。

考虑到您已经有了viewview model,我会在domain layer中创建PurchaseRecordsService类(请注意,根据您的情况,您可能不会使用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());
   }
}

最后一条-我们需要在PurchaseController 中定义我们的Action

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

概括一下,我们所拥有的是松散耦合的代码,我们的PresentationData Access层互不了解,它们只依赖于Domain层。如果需要,您可以用WPF替换MVC前端。例如,从EF转移到另一种技术,您的代码是可测试的。

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

但请记住,当涉及到将某些逻辑放在哪里的问题时,个人偏好会起很大作用。