20

我继承了一个 MVC2 项目,使用了一个非常标准且保存完好的 DDD 模式。我也对整个 DTO/ViewModel 辩论进行了大量阅读。

目前我们的 DTO 经常像ViewModels一样使用。老实说,这对我们正在做的事情没有影响,但我想在升级网站时使用正确的 ViewModel。

这是我的问题:

我们的“域”项目模型当前持有实体并将 DTO 返回给我的控制器。现在我需要将该 DTO 映射到 ViewModel。我应该在哪里做这个?

  • 就在控制器里?
  • 在域项目中?
  • 别处?

我将我的 ViewModel 与我们的“Web”项目中的视图放在一起,因此在域项目中转换DTO -> ViewModel感觉不对。在控制器中这样做也感觉不对。

别人做了什么?

编辑:

这个问题/答案建议在控制器中处理它。这肯定很容易过度思考。

4

4 回答 4

18

DTO 通常是特定于技术的。例如,在 .NET 世界中,您的 DTO 可能带有序列DataContractDataMember属性。此外,DTO 与返回它们的服务一起形成了六边形架构的域适配器。它们使您的域适应特定的传输技术,例如 HTTP,因此它们位于您的域之外。换句话说,域不应该了解 DTO - DTO 应该在单独的项目中定义。包含该服务的项目应该具有将域对象映射到 DTO 的映射代码。

ASP.NET MVC 项目在本质上是相似的——它使您的服务/DTO(或直接域对象)适应一种表示技术,尤其是 HTML。因此,DTO 不应该知道 ViewModel。相反,MVC 控制器应该调用 DTO 和 ViewModel 之间的映射。这可以通过多种方式完成,但我发现效果最好的是 ViewModel 中的构造函数接受 DTO。此外,在控制器操作保证创建 DTO 以发送回服务的情况下,ViewModel 可以包含基于 ViewModel 创建 DTO 的方法。这包含 ViewModel 中最接近实际数据的所有映射代码 -信息专家模式的一个实例。实现这一点的另一种方法是使用类似的东西AutoMapper使用基于约定的映射来避免样板代码。除非有必要,否则我会认为除此之外的任何东西都是矫枉过正的。

在许多情况下,您的 ViewModel 最终看起来就像 DTO,但具有用于绑定和验证的 ASP.NET MVC 特定属性。尽管这似乎违反了DRY,但这些确实是单独的职责。

于 2012-08-09T00:14:41.113 回答
6

首先,始终为您的视图使用显式视图模型,不要将 DTO 一直传递到视图。它需要更多的前期工作,但它可以让您更好地控制视图中确切需要哪些数据(它还可以防止像 EF 这样的框架侧载大量您可能使用或可能不使用的额外数据)

其次,本文概述了 Orchestrator 模式http://www.simple-talk.com/dotnet/asp.net/never-mind-the-controller,-here-is-the-orchestrator/这可能只是其他一些模式,但我喜欢这种格式。

本质上,您为每个 Controller 创建了一个 Orchestrator。Orchestrator 接收数据(通常是 ViewModel,以及所需的任何其他基本数据类型,尤其是来自 HttpContext 的数据),并返回 ViewModel(如果 View 需要,否则返回一些其他类型)。

这种格式使您能够轻松地对实际逻辑进行单元测试,而无需尝试模拟控制器所需的 HttpContext 内容。

于 2012-08-08T22:39:20.040 回答
4

听起来像是您想在专门构建的映射类/模块中做的事情。

我会亲自让我的控制器依赖于映射服务,然后在使用新映射的视图模型返回视图之前将实际转换委托给该服务。

public class DemoController : Controller
{
    private readonly IMappingService _mappingService;

    public DemoController(IMappingService mappingService)
    {
        _mappingService = mappingService;
    }

    public ActionResult Stuff()
    {
        var vm = _mappingService.Map(yourDto);

        return View(vm);
    }
}
于 2012-08-08T22:33:05.967 回答
1

一个不错的方法是使用第二个构造函数重载 ViewModel 上的构造函数,该构造函数将 dto 作为参数。这意味着您可以在视图模型本身中处理映射。这使您的控制器保持整洁,而无需设置映射服务。

于 2012-08-08T22:53:13.077 回答