如何在POST操作中将视图模型映射回域模型?

互联网上使用ViewModels和Automapper的每篇文章都给出了“Controller - > View”方向映射的指导原则。 将域模型和所有选择列表一起放入一个专门的ViewModel中,并将其传递给视图。 这很清楚,很好。
该视图有一个表单,最终我们在POST操作中。 在这里,所有的模型绑定器都与[明显]另一个View模型一起出现,它至少在命名约定的部分中与原始ViewModel明显相关,以便进行绑定和验证。

你如何将它映射到你的域模型?

让它成为插入动作,我们可以使用相同的Automapper。 但如果这是更新行为呢? 我们必须从存储库中检索我们的域实体,根据ViewModel中的值更新其属性并保存到存储库。

附录1(2010年2月9日):有时,分配模型的属性是不够的。 根据View Model的值,应该对域模型采取一些措施。 也就是说,应该在域模型上调用一些方法。 可能应该有一种应用程序服务层,它位于控制器和域之间以处理视图模型...


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

  • 保持控制器很薄
  • 尊重SoC实践
  • 遵循域驱动设计原则
  • 是干的
  • 未完待续 ...

  • 我使用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);
    }
    

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

  • POST数据查看模型+验证在ModelBinder中完成(可以使用自定义绑定进行验证)
  • 错误处理(即通过Repository捕获数据访问异常抛出)可以通过[HandleError]过滤器完成
  • 控制器操作非常简单,关注点是分开的:映射问题在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?