从视图模型到领域模型的最佳映射位置在哪里?

15

最佳的ViewModel到Domain Model映射位置是哪里?在这里所说的映射是从我的EditGrantApplicationViewModel到一个GrantApplication对象。

假设我有以下操作方法(部分代码):

[HttpPost]
public ActionResult Create(EditGrantApplicationViewModel editGrantApplicationViewModel)
{
   if (!ModelState.IsValid)
   {
      return View("Create", editGrantApplicationViewModel);
   }

   return View("Index");
}

我需要将editGrantApplicationViewModel传递给服务层方法并在该方法中进行映射吗?


@Sergi - 他的意思是将ViewModel中的值映射到Model实体中。 - UpTheCreek
4个回答

24
您不应该在服务层中放置任何映射逻辑,因为它根本不属于那里。映射逻辑应该放在控制器中,而不是其他地方。
你可能会问为什么?很简单,通过将映射逻辑放在服务层中,它需要知道ViewModels,而服务层永远不应该知道它们——同时,在其中放置映射逻辑会降低应用程序的灵活性,因为您不能重复使用服务层而不进行大量修改。
相反,您应该这样做:
// Web layer (Controller)
public ActionResult Add(AddPersonViewModel viewModel)
{
    service.AddPerson(viewModel.FirstName, viewModel.LastName)
    // some other stuff...
}

// Service layer
public void AddPerson(string firstName, string lastName)
{
    var person = new Person { FirstName = firstName, LastName = lastName };
    // some other stuff...
}

通过以上操作,您可以使服务层更加灵活,因为它不会绑定到特定的类并且不知道视图模型的存在。
更新:
要将从服务层返回的实体映射到视图模型,您可能需要查看AutomapperValue Injecter

我很感兴趣的是,如果我的视图模型相当复杂,我想将其映射到领域模型。我的意思不仅是名字和姓氏,而是还有其他6个属性。我不想让服务方法接收6个参数,也不想引入第三个类(领域模型和视图模型之后)来表示基本相同的内容。 - Zoltán Tamási
Zoltan的问题与我相同。我希望能够将对象从Web层传递到服务层,而不必创建另一组对象。 - Aaron

4

1

在你的WebUI层中完成,但不要在控制器中完成,而是在其中调用你的自定义映射器/构建器接口/类

示例: http://prodinner.codeplex.com


0
我个人永远不会将视图模型传递到服务层。如果你走这条路,你的服务就会直接了解在视图上显示的内容。这将导致你的视图模型的更改引起你的服务层的更改。
例如: 假设你决定为授权编辑原因添加一个 SelectList 到你的视图模型中。
public class EditGrantApplicationViewModel
{
   //...
   public SelectList ReasonForEdit {get;set;}
   //...
}

这可能是一个完全有效的要求,但请问一下自己:将 SelectList 传递到服务层是否有意义?SelectList 更多地属于 UI 领域,对于服务层来说并没有实际意义。服务层只关心原因,而不关心选择列表。
我会取出您的视图模型,提取所需信息,然后将该单元传递到您的服务层。
[HttpPost]
public ActionResult Create(EditGrantApplicationViewModel editGrantApplicationViewModel)
{
   if (!ModelState.IsValid)
   {
      return View("Create", editGrantApplicationViewModel);
   }

   GrantApplication grantApplication = new GrantApplication();
   grantApplication. // other fields.
   grantApplication.Reason = editGrantApplicationViewModel.ReasonForEdit.SelectedValue;
   grantApplication. // other fields.
   _someService.EditApplication(grantApplication);

   return View("Index");
}

如果你还没有看过,可以查看AutoMapper,它可以帮助在视图模型、数据传输对象和其他类之间进行转换。


@Mark:我之前和一个人交谈过,他说你的服务层必须做所有事情,包括映射,所以我问映射应该在哪里进行。那么我想行动方法是最好的地方了? - Brendan Vogt
@Mark:我确实使用AutoMapper,但我不喜欢它的一件事情(可能是由于缺乏知识),就是当我在两个对象中都有一个FirstName属性时,如果源FirstName为空,而目标FirstName为“Brendan”,那么映射后的目标FirstName会变为空。希望这样说得清楚? - Brendan Vogt
@Jfar,我并不建议在将领域对象映射到视图模型或DTO时使用Automapper来走捷径。如果这些类相似且您经常这样做,则使用automapper定义一个CreateMap每次产生相同结果似乎是一种不错的额外收益。有时候Automapper不是完成工作的正确工具,需要编写从左到右的代码,我只是让OP意识到可能有一个有用的工具可帮助他的项目。他可以权衡利弊,决定是否适合他项目的需求。 - Mark Coleman
@Mark - “我不建议使用Automapper来简化从domain到viewmodel的映射” - 什么意思? “为了帮助从ViewModel到GranteApplication的映射,你可以考虑使用AutoMapper,因为它可能会减少映射代码的量。” - John Farrell
我认为你的视图模型中甚至不应该有"SelectList",你最多只需要包含可选择项目,然后将它们绑定到视图中的某个控件上,这可以是选择列表、单选按钮,或者更进一步,基于热键的控制台应用程序选择。因此,我认为改变GUI控件类型甚至不应该影响视图模型。 - Zoltán Tamási
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接