2

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

楷模:

我有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;
        }
    }
4

3 回答 3

3

长手响应

[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 的文件夹中,而不是 Models 中。并使用 ViewModel 或 VM 附加所有视图模型。这也是一个很好的约定,imo,根据它们所针对的视图来命名您的视图模型,而不是它们将映射到的域模型,因为并非所有视图模型都会直接映射到域模型。有时,您会需要多个域模型的一部分,用于单个视图,这会破坏您的命名约定。

因此,在这种特殊情况下,我建议将视图模型版本更改Admin.ModelsAdmin.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();
}
于 2013-03-08T18:04:11.703 回答
2

查看一个名为AutoMapper的库。

从他们的维基:

什么是 AutoMapper?
AutoMapper 是一个简单的小库,旨在解决一个看似复杂的问题——摆脱将一个对象映射到另一个对象的代码。这种类型的代码写起来相当枯燥乏味,那么为什么不发明一个工具来为我们做呢?

于 2013-03-08T17:29:14.887 回答
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 中的代码,但使用扩展代替。

于 2014-08-20T23:30:15.170 回答