如何在POST操作中将视图模型映射回域模型?
互联网上使用ViewModels和Automapper的每篇文章都给出了“Controller - > View”方向映射的指导原则。 将域模型和所有选择列表一起放入一个专门的ViewModel中,并将其传递给视图。 这很清楚,很好。
该视图有一个表单,最终我们在POST操作中。 在这里,所有的模型绑定器都与[明显]另一个View模型一起出现,它至少在命名约定的部分中与原始ViewModel明显相关,以便进行绑定和验证。
你如何将它映射到你的域模型?
让它成为插入动作,我们可以使用相同的Automapper。 但如果这是更新行为呢? 我们必须从存储库中检索我们的域实体,根据ViewModel中的值更新其属性并保存到存储库。
附录1(2010年2月9日):有时,分配模型的属性是不够的。 根据View Model的值,应该对域模型采取一些措施。 也就是说,应该在域模型上调用一些方法。 可能应该有一种应用程序服务层,它位于控制器和域之间以处理视图模型...
如何组织这些代码以及将其放置在何处以实现以下目标?
我使用IBuilder接口并使用ValueInjecter实现它
public interface IBuilder<TEntity, TViewModel>
{
TEntity BuildEntity(TViewModel viewModel);
TViewModel BuildViewModel(TEntity entity);
TViewModel RebuildViewModel(TViewModel viewModel);
}
...(实现)RebuildViewModel只是调用BuildViewModel(BuilEntity(viewModel))
[HttpPost]
public ActionResult Update(ViewModel model)
{
if(!ModelState.IsValid)
{
return View(builder.RebuildViewModel(model);
}
service.SaveOrUpdate(builder.BuildEntity(model));
return RedirectToAction("Index");
}
顺便说一句,我不写写ViewModel我写输入因为它更短,但这并不重要
希望能帮助到你
更新:我现在在ProDinner ASP.net MVC Demo App中使用这种方法,现在它被称为IMapper,还提供了一个pdf,其中详细解释了这种方法
像AutoMapper这样的工具可以用来源对象的数据更新现有的对象。 控制器的更新操作可能如下所示:
[HttpPost]
public ActionResult Update(MyViewModel viewModel)
{
MyDataModel dataModel = this.DataRepository.GetMyData(viewModel.Id);
Mapper<MyViewModel, MyDataModel>(viewModel, dataModel);
this.Repostitory.SaveMyData(dataModel);
return View(viewModel);
}
除了上面代码片段中可见的内容之外:
控制器操作非常简单,关注点是分开的:映射问题在AutoMapper配置中解决,验证由ModelBinder完成,通过Repository进行数据访问。
我想说的是,在客户端交互的两个方向上重用术语ViewModel。 如果你已经阅读了足够的ASP.NET MVC代码,你可能已经看到了ViewModel和EditModel之间的区别。 我认为这很重要。
ViewModel表示呈现视图所需的所有信息。 这可能包括在静态非交互式场所呈现的数据,以及纯粹用于执行检查以确定究竟要呈现的数据的数据。 控制器GET操作通常负责为其视图打包ViewModel。
一个EditModel(或者一个ActionModel)表示执行用户想要为该POST执行的操作所需的数据。 所以一个EditModel真的试图描述一个动作。 这可能会排除ViewModel中的一些数据,虽然相关,但我认为认识到它们确实不同是很重要的。
一个想法
也就是说,你可以非常容易地从Model - > ViewModel获得一个AutoMapper配置,而从EditModel - > Model获得一个不同的配置。 然后不同的Controller动作只需要使用AutoMapper。 地狱EditModel可以有一个函数来验证它的模型属性,并将这些值应用到模型本身。 它没有做其他任何事情,并且您在MVC中拥有ModelBinder以将请求映射到EditModel。
另一个想法
除此之外,我最近一直在想的是,ActionModel这种想法的一个缺点是,客户向您发布的内容实际上是用户执行的多个操作的描述,而不仅仅是一个大数据集。 这当然需要一些Javascript在客户端进行管理,但这个想法很有趣,我认为。
基本上,当用户在屏幕上执行操作时,Javascript会开始创建一个操作对象列表。 一个例子是用户可能在员工信息屏幕上。 他们更新姓氏并添加新地址,因为该雇员最近已结婚。 在封面下面,这产生一个ChangeEmployeeName
和一个AddEmployeeMailingAddress
对象到一个列表。 用户单击“保存”提交更改,并提交两个对象的列表,每个对象只包含执行每个操作所需的信息。
您需要一个更智能的ModelBinder,然后使用默认的ModelBinder,但是良好的JSON序列化程序应该能够处理客户端操作对象到服务器端的映射。 服务器端的(如果你在一个2层环境中的话)可以很容易地拥有完成他们所使用模型的动作的方法。 因此,Controller操作最终只需获取Model实例的Id以及要执行的操作列表。 或者这些行为在他们身上有id使他们保持独立。
所以也许在服务器端实现这样的事情:
public interface IUserAction<TModel>
{
long ModelId { get; set; }
IEnumerable<string> Validate(TModel model);
void Complete(TModel model);
}
[Transaction] //just assuming some sort of 2-tier with transactions handled by filter
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
var errors = new List<string>();
foreach( var action in actions )
{
// relying on ORM's identity map to prevent multiple database hits
var employee = _employeeRepository.Get(action.ModelId);
errors.AddRange(action.Validate(employee));
}
// handle error cases possibly rendering view with them
foreach( var action in editModel.UserActions )
{
var employee = _employeeRepository.Get(action.ModelId);
action.Complete(employee);
// against relying on ORMs ability to properly generate SQL and batch changes
_employeeRepository.Update(employee);
}
// render the success view
}
这确实使回发操作相当通用,因为您依靠ModelBinder来获取正确的IUserAction实例和您的IUserAction实例,以便自己执行正确的逻辑或(更可能)使用info调用模型。
如果您处于三层环境中,则可以将IUserAction制作成简单的DTO,以跨越边界进行拍摄,并在应用层以类似的方式执行。 根据你如何做这一层,它可以很容易地拆分,并仍然保留在一个交易中(想到的是Agatha的请求/响应,并利用DI和NHibernate的身份映射)。
无论如何,我确定这不是一个完美的想法,它需要客户端的一些JS来管理,我还没有能够做一个项目,但看不到它如何展开,但这篇文章试图思考如何到那里然后再回来,所以我想我会给我的想法。 我希望它有帮助,我很乐意听到其他管理互动的方法。
链接地址: http://www.djcxy.com/p/21497.html上一篇: How to map View Model back to Domain Model in a POST action?
下一篇: How to achieve reusable user controls and maintain DRY?