在何处将DTO转换为ViewModel?

时间:2012-08-08 22:24:33

标签: asp.net-mvc domain-driven-design viewmodel dto separation-of-concerns

我使用一个非常标准且保存得很好的DDD模式继承了一个MVC2项目。我也在整个DTO / ViewModel辩论中做了很多阅读。

目前我们的DTO经常被用作 ViewModels 。老实说,我们正在做的事情并没有什么不同,但我想在升级网站时使用正确的ViewModel。

这是我的问题:

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

  • 在控制器中?
  • 在域项目中?
  • 其他地方?

我将ViewModel与我们的“Web”项目中的视图保持一致,因此转换 DTO - >感觉不对。域项目中的ViewModel 。在控制器中执行此操作也感觉不对。

其他人做了什么?

修改

This question/answer 建议在控制器中处理它。很容易想到这一点。

4 个答案:

答案 0 :(得分:16)

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

ASP.NET MVC项目本质上是类似的 - 它将您的服务/ DTO(或域对象直接)适应于表示技术,特别是HTML。因此,DTO不应该知道ViewModels。相反,MVC控制器应该调用DTO和ViewModel之间的映射。这可以通过多种方式完成,但我发现效果最好的是ViewModel中接受DTO的构造函数。此外,在控制器动作保证创建要发送回服务的DTO的情况下,ViewModel可以包含用于基于ViewModel创建DTO的方法。这包含最接近实际数据的ViewModel中的所有映射代码 - information expert pattern的实例。实现这一点的另一种方法是使用AutoMapper之类的东西,它使用基于约定的映射来避免样板代码。除非有人要求,否则我会考虑过度杀伤。

在许多情况下,您的ViewModel最终看起来就像DTO一样,但具有用于绑定和验证的ASP.NET MVC特定属性。即使这似乎违反了DRY,但这些责任实际上是分开的。

答案 1 :(得分:6)

首先,始终为您的视图使用显式ViewModel,不要将DTO一直传递到View。这是一个更前期的工作,但它可以让你更好地控制完全视图中需要什么数据(它还可以阻止像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内容。

答案 2 :(得分:4)

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

我个人会给我的控制器依赖一个映射服务,然后在用新映射的viewmodel返回视图之前委托实际的转换。

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);
    }
}

答案 3 :(得分:1)

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