如何在POST操作中将View Model映射回Domain Model?

Ant*_*kov 85 asp.net-mvc domain-driven-design separation-of-concerns viewmodel automapper

互联网上使用ViewModels并使用Automapper的每篇文章都提供了"Controller - > View"方向映射的指导.您将域模型与所有选择列表一起放入一个专门的ViewModel并将其传递给视图.这很清楚也很好.
视图有一个表单,最终我们处于POST操作中.这里所有的模型绑定器与[显然]另一个视图模型一起来到现场,它显然与原始的ViewModel 相关,至少在命名约定方面是为了绑定和验证.

如何将其映射到您的域模型?

让它成为插入动作,我们可以使用相同的Automapper.但如果是更新动作怎么办?我们必须从Repository中检索我们的Domain Entity,根据ViewModel中的值更新它的属性并保存到Repository.

附录1(2010年2月9日):有时,分配模型的属性是不够的.应根据View Model的值对域模型采取一些措施.即,应该在域模型上调用一些方法.可能应该有一种位于Controller和Domain之间的应用服务层,以便处理View Models ...


如何组织此代码以及将其放置在何处以实现以下目标?

  • 保持控制器薄
  • 荣誉SoC实践
  • 遵循领域驱动设计原则
  • 干嘛
  • 未完待续 ...

Omu*_*Omu 37

我使用IBuilder接口并使用ValueInjecter实现它

public interface IBuilder<TEntity, TViewModel>
{
      TEntity BuildEntity(TViewModel viewModel);
      TViewModel BuildViewModel(TEntity entity);
      TViewModel RebuildViewModel(TViewModel viewModel); 
}
Run Code Online (Sandbox Code Playgroud)

...(实现)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");
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句我不写ViewModel我写输入因为它更短,但这并不重要
希望它有所帮助

更新: 我现在在ProDinner ASP.net MVC演示应用程序中使用此方法,现在称为IMapper,还提供了一个pdf,详细解释了这种方法

  • 我喜欢这种方法,我在这里实现了一个示例:https://gist.github.com/2379583 (6认同)

Pan*_*nek 7

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);
}
Run Code Online (Sandbox Code Playgroud)

除了上面的代码段中可见的内容:

  • 用于查看模型+验证的POST数据在ModelBinder中完成(可以使用自定义绑定进行扩展)
  • 错误处理(即通过存储库捕获数据访问异常抛出)可以通过[HandleError]过滤器完成

控制器操作相当薄,问题分离:映射问题在AutoMapper配置中得到解决,验证由ModelBinder完成,而数据访问由Repository完成.

  • 我不确定Automapper在这里是否有用,因为它不能反转扁平化.毕竟,Domain Model不是像View Model这样简单的DTO,因此为它分配一些属性可能还不够.可能应该根据View Model的内容对域模型执行某些操作.但是,+1用于分享相当不错的方法. (6认同)

Sea*_*ver 5

我想说你在客户端交互的两个方向上重复使用术语ViewModel.如果你已经阅读了足够多的ASP.NET MVC代码,你可能已经看到了ViewModel和EditModel之间的区别.我认为这很重要.

ViewModel表示呈现视图所需的所有信息.这可能包括在静态非交互式位置呈现的数据以及纯数据执行检查以确定要呈现的内容.Controller GET操作通常负责为其View打包ViewModel.

EditModel(或者可能是ActionModel)表示执行用户想要为该POST执行的操作所需的数据.所以EditModel真的试图描述一个动作.这可能会从ViewModel中排除一些数据,虽然相关,但我认为重要的是要意识到它们确实是不同的.

一个想法

这就是说你可以非常容易地使用AutoMapper配置来从Model - > ViewModel和另一个来自EditModel - > Model.然后不同的Controller动作只需要使用AutoMapper.地狱EditModel可以在其上有一个函数来验证它对模型的属性,并将这些值应用于模型本身.它没有做任何其他事情,你在MVC中有ModelBinder,无论如何都要将Request映射到EditModel.

另一个想法

除了我最近一直在思考的东西,就像ActionModel的想法一样,客户端发回给你的实际上是用户执行的几个动作的描述,而不仅仅是一个大的数据.这肯定需要客户端的一些Javascript来管理,但我认为这个想法很有趣.

基本上,当用户在您呈现它们的屏幕上执行操作时,Javascript将开始创建一个操作对象列表.一个例子可能是用户在员工信息屏幕上.他们更新姓氏并添加新地址,因为该员工最近已经结婚.在封面下,这会生成一个列表对象ChangeEmployeeName和一个AddEmployeeMailingAddress对象.用户单击"保存"以提交更改,并提交两个对象的列表,每个对象仅包含执行每个操作所需的信息.

你需要一个更智能的ModelBinder然后是默认的,但是好的JSON序列化器应该能够处理客户端操作对象到服务器端的映射.服务器端(如果您处于双层环境中)可以轻松地拥有完成对其使用的模型的操作的方法.因此,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
}
Run Code Online (Sandbox Code Playgroud)

这真的使得回发操作相当通用,因为你依靠你的ModelBinder来获得正确的IUserAction实例和你的IUserAction实例来执行正确的逻辑本身或(更有可能)用信息调用模型.

如果您处于3层环境中,IUserAction可以简单地在边界上拍摄简单的DTO,并在应用层上以类似的方法执行.根据你如何做这个层,它可以很容易地分开并仍然保留在一个事务中(想到的是Agatha的请求/响应并利用DI和NHibernate的身份图).

无论如何,我确定这不是一个完美的想法,它需要客户端的一些JS来管理,我还没有能够做一个项目尚未看到它如何展开,但该帖子试图考虑如何到达那里然后再回来所以我想我会给出我的想法.我希望它有所帮助,我很想听听其他管理互动的方法.