如何将DTO转换为View Model然后再返回?

时间:2013-03-08 17:27:58

标签: asp.net-mvc entity-framework asp.net-mvc-4 repository-pattern asp.net-mvc-viewmodel

我正在使用MVC 4进行存储库模式和单元测试。我有一个典型的控制器,具有简单的CRUD功能。我将我的视图模型与我的DTO分开了,我想知道在2之间进行转换的最佳方式:

型号:

我的Admin.Models.Product是我的视图模型,AdminAssembly.Models.Product是我的DTO。

控制器:

    //repo that handles product operations
    AdminAssembly.Interfaces.IEntityRepository<AdminAssembly.Models.Product> db;
    //default constructor
    public ProductController() { db = new AdminAssembly.Repositories.EntityRepo<AdminAssembly.Models.Product>(new AdminAssembly.Models.EntitiesContext()); }
    //unit testing constructor
    public ProductController(AdminAssembly.Interfaces.IEntityRepository<AdminAssembly.Models.Product> context) { db = context; }

    //
    // POST: /Product/Create

    [HttpPost]
    public ActionResult Create(Admin.Models.Product product) {

        if (ModelState.IsValid) {
            //COMPILE-ERROR: how to convert to DTO?
            db.Add(product);
        }
        return View();

    }

    //
    // GET: /Product/Edit/5

    public ActionResult Edit(int id) {
        //COMPILE-ERROR: how to convert to view model?
        Admin.Models.Product product = db.GetAll().Where(p => p.ID == id);

        return View(product);
    }

如何在2?

之间进行转换

我是否在我的视图模型中引用我的DTO程序集并执行以下操作:(这不会破坏我的单元测试吗?)

//convert to AdminAssembly.Models.Product
db.Add(product.ToDTO());

//convert back to Admin.Models.Product via constructor
Admin.Models.Product product = Admin.Models.new Product(db.GetAll().Where(p => p.ID == id));

我是否需要某种对象转换黑盒?

Converter.ToViewProduct(product);

某种界面?

或其他什么?

更新1:

    public static class Product {
        public static Admin.Models.Product ToView(AdminAssembly.Models.Product dto) {
            Admin.Models.Product viewProduct = new Admin.Models.Product();
            //straight copy
            viewProduct.Property1 = dto.Property1;
            viewProduct.Property2 = dto.Property2;

            return viewProduct;
        }
        public static AdminAssembly.Models.Product ToDTO(Admin.Models.Product viewModel) {
            AdminAssembly.Models.Product dtoProduct = new AdminAssembly.Models.Product();
            //straight copy
            dtoProduct.Property1 = viewModel.Property1;
            dtoProduct.Property2 = viewModel.Property2;

            //perhaps a bit of wizza-majig
            dtoProduct.Property1 = viewModel.Property1 + viewModel.Property2;

            return dtoProduct;
        }
    }

3 个答案:

答案 0 :(得分:2)

查看名为AutoMapper的图书馆。

来自他们的维基:

  

什么是AutoMapper?
  AutoMapper是一个简单的小型库,用于解决一个看似复杂的问题 - 摆脱将一个对象映射到另一个对象的代码。这种类型的代码是相当沉闷和无聊的写,所以为什么不发明一个工具来为我们做?

答案 1 :(得分:2)

长期回应

[HttpPost]
public ActionResult Create(Admin.Models.Product product) 
{
  if (ModelState.IsValid)
  {
    //COMPILE-ERROR: how to convert to DTO?
    var dtoProduct = new AdminAssembly.Models.Product();
    dtoProduct.Property1 = product.Property1;
    dtoProduct.Property2 = product.Property2;
    //...and so on
    db.Add(dtoProduct);
  }
  return View();
}

虽然这看起来冗长乏味(而且确实如此),但最终还是要发生在某个地方。

您可以在另一个类或扩展方法中隐藏此映射,或者您可以使用像AutoMapper这样的第三方,正如Charlino指出的那样。

作为旁注,在两个不同的命名空间中有两个具有相同名称的类最终会让人感到困惑(如果不适合你,那么下一个必须维护代码的人。)尽可能实现更友好和更具描述性的名称。例如,将所有视图模型放在名为ViewModels的文件夹中,而不是模型。并使用ViewModel或VM附加所有视图模型。它也是一个很好的约定,imo,根据它们所针对的视图命名您的视图模型,而不是它们将被映射到的域模型,因为并非所有视图模型都将直接映射到域模型。对于单个视图,有时您会想要多个域模型的一部分,这会破坏您的命名约定。

因此,在这种特殊情况下,我建议将Admin.Models更改为Admin.ViewModels,然后将Product的视图模型版本重命名为CreateViewModel。您的代码将更具可读性,并且在您的方法中不会出现名称空间。

所有这些都会导致一个看起来更像这样的方法:

[HttpPost]
public ActionResult Create(CreateViewModel viewModel) 
{
  if (ModelState.IsValid)
  {
    var product = new Product();
    product.Property1 = viewModel.Property1;
    product.Property2 = viewModel.Property2;
    //...and so on
    db.Add(product);
  }
  return View();
}

答案 2 :(得分:0)

如果您不想使用AutoMapper,可以使用@ Forty-Two建议的扩展名。如果要映射的东西数量不是很多,我会采用这种方法,因为那时,AutoMapper == YAGNI

public static class Extensions
{
    public static ViewModel ToViewModel(this Model )
    {
        var vm = new ViewModel()
        {
            //map
        };
        return vm;
    }
    public static Model ToModel(this ViewModel viewModel)
    {
        var model = new Model()
        {
            //map
        };
        return model;
    }
}

与UPDATE中的代码类似,但使用扩展名。