通过AutoMapper映射领域模型到视图模型,是或否?

21

我希望使用视图模型来显示,而不是领域模型。我想自定义一个属性以供显示,应该如何操作?使用AutoMapper用于显示是否是一个好的实践?

以下是代码示例:

public class BookController : BaseController
    {
        private IBookService bookService;

        public BookController(IBookService bookService)
        {
            this.bookService = bookService;
        }

        public ActionResult Details(int id)
        {
            var book = bookService.GetBookById(id);

            return View(Mapper.Map<BookView>(book));
        }
}

    public class Book 
    {        
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
    }

    public class BookView
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

如果我采用其他方式,就可以自定义任何属性,例如如下:

  public ActionResult Details(int id)
        {
            var book = bookService.GetBookById(id);

            return View(new BookView(book));
        }

    public class BookView
    {
       public BookView(Book book){
         Name = book.Name +" Decorated";
       }
        public int Id { get; set; }
        public string Name { get; set; }
    }

我该怎么做?使用AutoMapper在展示方面是一个好的实践吗?

更新

看起来在下面这种场景中使用AutoMapper更为合适。例如,像下面这样将视图模型映射到领域模型。有什么意见吗?

  [HttpPost]
    public ActionResult Create(BookView bookView)
    {
        try
        {
            var book = Mapper.Map<Book>(bookView);  //this is wrong

            bookService.SaveOrUpdate(book);

            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }

更新 2

对于通过视图模型进行复杂自定义显示的情况,我不想使用 AutoMapper 来映射显示逻辑,即使 AutoMapper 可以映射它。因为这样会混淆不同的目的。例如:

Mapper.CreateMap<Book, BookView>()
    .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name + " this is for display purpose"));

然而,像下面这样使用手动映射似乎很直观。

 public BookView(Book book){
    //mapping here
}

更新3

引用自Jimmy Bogard

我认为只因为你不想使用 " = " 运算符就使用 AutoMapper 是有点懒。相反,我们使用它来展平和重塑,优化目标类型的环境。请记住,我创造 AutoMapper 的最初动机是:

通过映射到 DTOs 实现保护域层免受其他层的影响

感谢 @AndrewWhitaker 提供的链接


2
根据更新2,它是如何混合不同的目的呢?ViewModel的目的不是显示领域模型吗?Automapper只是实现这一目的的工具。 - Andrew Whitaker
与其他映射(例如域到DTO)相比,它更加简洁,更不易出错。 - Pingpong
你需要为领域 --> DTO 创建完全不同的映射。此外,你不应该使用 AutoMapper 将任何东西映射到领域。 - Andrew Whitaker
1个回答

24
这是AutoMapper的一个很好的使用案例(我在许多项目中广泛使用它并取得了成功)。通常情况下,您不希望将领域实体暴露给视图(在MVC中,这将直接将模型暴露给视图,这是不正确的)。
您不需要在领域实体和视图模型之间建立1对1的映射关系。您可以使它们看起来完全不同,并在CreateMap<>调用中自定义映射。以您的示例为例:
Mapper.CreateMap<Book, BookView>()
    .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name + " Decorated"));

最坏的情况下,你可以放弃使用 automapper,针对复杂情况使用自定义类型解析器或结合 automapper 使用自定义类型解析器完成工作。

事实上 Jimmy Bogard (作者) 建议这样使用 AutoMapper 。他特别提到将域实体映射到用于强类型视图的 ASP.NET MVC 视图模型。

另一个优点是,你可以对映射配置进行单元测试。这样,如果 ViewModel 与 Model 不匹配,就会出现单元测试失败。

更新:

我认为你在问题中添加的引言进一步支持了使用 AutoMapper 将领域模型映射到 ViewModels:

相反,我们使用它来展开和重塑,为目标类型的环境进行优化。

因此,在我的例子中,你肯定会为目标类型的环境进行优化(在本例中为视图)。

根据我上面提到的链接,您不应该使用automapper将数据从View映射到领域模型中,而是只能从领域模型中映射到View。因此,无论如何,您都需要编写一些逻辑来创建/更新领域实体以便从View中接收数据。请记住,控制器操作不应直接获取领域实体(您不应信任直接来自视图的数据-让模型来确定是否领域实体有效)。

1
根据您的更新(有帮助),是否有将视图模型转换为领域模型,并进行视图模型验证的示例或链接? - Pingpong
我不理解你最后一段的意思。你的意思是说我们不应该使用automapper从ViewModel映射到domain(POCO)吗?如果是这样,那么如何将用户输入(View)保存到数据库(Model)?需要手动进行映射吗? - Willy
@Willy: 是的,可以通过手动映射实现。想法是这个领域太重要了,不应该自动映射发生。 - Andrew Whitaker
为什么域对于自动映射来说太重要了?我还是很困惑。我的病人表上有50个或更多的字段。大多数字段必须在注册时填写。如果我手动映射,那会很无聊。这就是自动映射器简化映射的目的吗? - Willy
1
@Willy:当然你可以自由地使用AutoMapper。我认为Jimmy Bogard在我上面链接的文章中更好地解释了这个问题。 - Andrew Whitaker

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