谁在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方法
视图模型通常只是属性的愚蠢集合。填充视图模型通常位于服务层内部,如果没有,则位于操作方法内部。
这样想角色。
- 域模型是到数据库表的直接映射
- 视图模型是显示视图所需的特性的集合
- 服务层获取/使用一个或多个域模型并填充视图模型
- 服务层还可以采用视图模型并创建/更新一个或多个域模型
- 控制器动作方法是两者之间的粘合剂。它调用服务层来获取(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
相互通信,这样可以确保您的应用程序是maintainable
、extensible
和testable
。
此外,试着在系统的各个层之间画出清晰的分隔。例如,将EF entities
作为Domain Model Entites
不是一个好主意。你不希望你的business logic layer
依赖于data access layer
,这样想吧,如果在未来的某个时候,你正在远离EF
,使用其他ORM
甚至其他技术来存储和查询数据,会怎么样。您不想仅仅因为要更改data access layer
而更改business logic layer
。因此,在您的情况下,从单词到代码。
考虑到您已经有了view
和view 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);
}
}
概括一下,我们所拥有的是松散耦合的代码,我们的Presentation
和Data Access
层互不了解,它们只依赖于Domain
层。如果需要,您可以用WPF
替换MVC
前端。例如,从EF
转移到另一种技术,您的代码是可测试的。
理想情况下,您的视图模型应该不知道您的域模型,所以我想说,您将填充逻辑放在控制器中,也许打包在某种映射/填充实用程序类中。
但请记住,当涉及到将某些逻辑放在哪里的问题时,个人偏好会起很大作用。