如何将editmodel / postmodel变为域模型

时间:2012-07-03 15:05:31

标签: asp.net-mvc viewmodel automapper editmodel

在ASP.NET MVC项目中,我们使用AutoMapper从域模型映射到viewmodel - 有时也会在执行此操作时展平层次结构。这就像一个魅力,使我们的观点的渲染逻辑非常精简和简单。

当我们想要从viewmodel(或postmodel或editmodel)转向域模型,,特别是在更新对象时时,会出现混淆。我们不能使用自动/双向映射,因为:

  1. 我们必须取消扁平化的层次结构
  2. 域模型上的所有属性都必须是可变的/具有公共setter
  3. 来自视图的更改并不总是将平面属性映射回域,但有时需要调用“ChangeManagerForEmployee()”或类似方法。
  4. Jimmy Bogards的文章The case for two-way mapping in AutoMapper中也对此进行了描述,但对此的解决方案没有详细描述,只是说明了:

      

    从EditModel到CommandMessages - 来自松散类型   EditModel为强类型,分解的消息。一个EditModel   可能会生成六条消息。

    在类似的SO question中,Mark Seeman有一个答案,他提到了

      

    我们使用抽象映射器和服务将PostModel映射到域对象

    但遗漏了细节 - 概念和技术实施。

    我们现在的想法是:

    1. 在控制器的操作方法中接收FormCollection
    2. 获取原始域模型并将其展平为viewModelOriginal和viewModelUpdated
    3. 使用UpdateModel()
    4. 将FormCollection合并到viewModelUpdated中
    5. 使用一些通用帮助方法将viewModelOriginal与viewModelUpdated进行比较
    6. A)生成CommandMessages a la Jimmy Bogard或B)通过属性和方法将差异直接变异到域模型(可能直接通过AutoMapper映射1-1属性)
    7. 有人可以提供一些示例,说明它们如何从FormCollection通过editmodel / postmodel进入域模型吗? “CommandMessages”或“抽象映射器和服务”?

4 个答案:

答案 0 :(得分:2)

我使用以下模式:

[HttpPost]
public ActionResult Update(UpdateProductViewModel viewModel)
{
    // fetch the domain model that we want to update
    Product product = repository.Get(viewModel.Id);

    // Use AutoMapper to update only the properties of this domain model
    // that are also part of the view model and leave the other properties unchanged
    AutoMapper.Map<UpdateProductViewModel, Product>(viewModel, product);

    // Pass the domain model with updated properties to the DAL
    repository.Update(product);

    return RedirectToAction("Success");
}

答案 1 :(得分:1)

您可能需要考虑CQRS(命令查询责任隔离 - 我认为这可能是您缺少的概念),甚至可能使用事件采购。

基本上是将读取逻辑与数据源分离并写入数据源的做法,甚至可能意味着有不同的数据模型进行读写。

这可能是一个很好的起点:http://abdullin.com/cqrs/

答案 2 :(得分:0)

选项C:将所有内容都放在控制器操作中。接下来,如果它变得多毛,则分解为服务(抽象映射器)或消息作为方法(命令消息方式)。

命令消息方式:

public ActionResult Save(FooSaveModel model) {
    MessageBroker.Process(model);

    return RedirectToAction("List");
}

处理器:

public class FooSaveModelProcessor : IMessageHandler<FooSaveModel> {

    public void Process(FooSaveModel message) {
        // Message handling logic here
    }

}

这实际上只是将表单的“处理”移出控制器操作并转移到单独的专用处理程序中。

但是,如果控制器动作变得多毛,我只会真正走这条路。否则,只需获取表单并根据需要对域模型进行适当的更新。

答案 3 :(得分:0)

这与我一直在做的事情有一些相似之处。我的视图模型的层次结构只是从它的域对象等价物中稍微扁平化了,但是我必须处理在保存时调用显式服务方法来做一些事情,比如添加到子集合,更改重要值等,而不是简单地反向映射。我还必须在快照之前和之后进行比较。

我的保存是将Ajax作为JSON发布到MVC操作,并将该操作神奇地绑定回MVC的视图模型结构。然后,我使用AutoMapper将顶级视图模型及其后代转换回其等效的域结构。我为客户端添加了新子项(我使用Knockout.js)并且需要调用显式服务方法的情况定义了许多自定义AutoMapper ITypeConverters。类似的东西:

            foreach (ChildViewModel childVM in viewModel.Children)
        {
            ChildDomainObject childDO = domainObject.Children.Where(cdo => cdo.ID.Equals(childVM.ID))).SingleOrDefault();
            if (childDO != null)
            {
                Mapper.Map<ChildViewModel, ChildDomainObject>(childVM, childDO);
            }
            else
            {
                MyService.CreateChildDO(someData, domainObject); // Supplying parent
            }
        }

我对删除做了类似的事情,这个过程在整个结构中很好地级联。我想平面结构可能更容易使用或更难 - 我有一个带有ID的AbstractDomainViewModel,我可以使用它进行上述匹配,这有助于。

我需要在更新之前和之后进行比较,因为我的服务层调用触发器验证,这会影响对象图的其他部分,这就决定了我需要返回的JSON作为Ajax响应。我只关心与UI相关的更改,因此我将保存的域对象转换回新的视图模型,然后使用辅助方法比较2个视图模型,使用手动前期检查和反射的组合。