9

在 ASP.NET MVC 项目中,我们使用 AutoMapper 从域模型映射到视图模型 - 有时还会在这样做的同时展平层次结构。这就像一个魅力,使我们的视图的渲染逻辑非常精简和简单。

当我们想要从视图模型(或后模型或编辑模型)到域模型的另一种方式时,尤其是在更新对象时,混乱就开始了。我们不能使用自动/双向映射,因为:

  1. 我们将不得不展开扁平的层次结构
  2. 域模型上的所有属性都必须是可变的/具有公共设置器
  3. 来自视图的更改并不总是被映射回域的平面属性,但有时需要调用诸如“ ChangeManagerForEmployee()”或类似的方法。

这也在 Jimmy Bogards 的文章中有所描述:AutoMapper 中的双向映射案例,但没有详细描述解决方案,只是他们去:

从 EditModel 到 CommandMessages——从松散类型的 EditModel 到强类型的中断消息。单个 EditModel 可能会生成六条消息。

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

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

但是细节 - 概念和技术实现 - 被遗漏了。

我们现在的想法是:

  1. 在控制器的操作方法中接收 FormCollection
  2. 获取原始域模型并将其展平为 viewModelOriginal 和 viewModelUpdated
  3. 使用将 FormCollection 合并到 viewModelUpdated 中UpdateModel()
  4. 使用一些通用的辅助方法来比较 viewModelOriginal 和 viewModelUpdated
  5. A)像 Jimmy Bogard 那样生成 CommandMessage 或 B)通过属性和方法将差异直接变异到域模型中(可能直接通过 AutoMapper 映射 1-1 属性)

有人可以提供一些示例,说明它们是如何从 FormCollection 通过editmodel/postmodel 到域模型的吗?“CommandMessages”还是“抽象映射器和服务”?

4

4 回答 4

2

I use the following pattern:

[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");
}
于 2012-07-03T15:23:50.533 回答
1

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

它基本上是一种分离从数据源读取和写入数据源的逻辑的做法,甚至可能意味着读取和写入具有不同的数据模型。

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

于 2012-07-03T23:28:48.053 回答
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 相关的更改,因此我将保存的域对象转换回新的视图模型,然后使用辅助方法来比较两个视图模型,结合使用手动前期检查和反射。

于 2012-07-06T05:51:32.213 回答
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
    }

}

这实际上只是将表单的“处理”从控制器动作中移到单独的、专门的处理程序中。

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

于 2012-07-04T04:01:40.587 回答