通常在一个应用程序中,我们开发人员会在两个不同的类型对象之间传输数据,通常我们会用DTOs(数据传输对象),View Models(视图模型),或者直接是一些从一个service或者Web API的一些请求或应答对象。一个常见的需要使用数据传输对象的情况是,我们想把属于一个对象的某些属性值赋值给另一个对象的某些属性值,但是问题是,这个两个对象可能并不是完全匹配的,比如,两者之间的属性类型,名称等等,是不一样的,或者我们只是想把一个对象的一部分属性值赋值给另一个对象。
public class Author
{
public string Name { get; set; }
}
public class Book
{
public string Title { get; set; }
public Author Author { get; set; }
}
public class BookViewModel
{
public string Title { get; set; }
public string Author { get; set; }
}
为了创建Book对象实例的一个View Model对象实例-BookViewModel对象实例,我们需要写如下代码:
BookViewModel model = new BookViewModel
{
Title = book.Title,
Author = book.Author.Name
}
上面的例子相当的直观了,但是问题也随之而来了,我们可以看到在上面的代码中,如果一旦在Book对象里添加了一个额外的字段,而后想在前台页面输出这个字段,那么就需要去在项目里找到每一处有这样转换字段的地方,这是非常繁琐的。另外,BookViewModel.Author是一个string类型的字段,但是Book.Author属性却是Author对象类型的,我们用的解决方法是通过Book.Auther对象来取得Author的Name属性值,然后再赋值给BookViewModel的Author属性,这样看起行的通,但是想一想,如果打算在以后的开发中把Name拆分成两个-FisrtName和LastName,那么,呵呵,我们得去把原来的ViewModel对象也拆分成对应的两个字段,然后在项目中找到所有的转换,然后替换。
使用AutoMapper
到现在,确切的说,AutoMapper的安装使用非常非常的便捷,就如同傻瓜照相机那样。你只需要从Nuget上下载AutoMapper的包到你的应用程序里,然后添加对AutoMapper命名空间的引用,然后你就可以在你的项目里随意使用它了。以下就是一个非常简单的是例子:
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<Book, BookViewModel>();
});
var model = Mapper.Map<Book,BookViewModel>(book);
使用AutoMappeer的好处是显而易见的,首先,不再需要我们去对DTO实例的属性一一赋值,然后无论你在Book对象或者BookViewModel对象里加了一个或者更多的字段,那都不会影响这个段映射的代码,我不再需要去找到每一处转换的地方去更改代码,你的程序会像之前正常运转。
不过,还是有个问题并没有得到很好的解决,这也是在AutoMapper文档上缺失的,为把Book.Athor.Name字段赋值给BookViewModel.Author字段,需要在每一处需要执行映射的代码地方,同时创建一个如下的显示转换申明代码,所以如果有很多处转换的话,那么我们就会写很多重复的这几行代码:
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<Book, BookViewModel>()
.ForMember(dest => dest.Author, opts => opts.MapFrom(src => src.Author.Name));
});
上面是从Book映射到BookViewModel。如果也需要BookViewModel映射到Book,怎么办呢?我们可以用下面方法进行处理,如果直接用下面代码直接映射的话, 程序会报错误。
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<Book, BookViewModel>()
.ForMember(dest => dest.Author, opts => opts.MapFrom(src => src.Author.Name));
cfg.CreateMap<BookViewModel, Book>()
.ForMember(dest => dest.Author, opts => opts.MapFrom(src => src.Author));
});
可以用如下方法:
方法1:
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<Book, BookViewModel>()
.ForMember(dest => dest.Author, opts => opts.MapFrom(src => src.Author.Name));
cfg.CreateMap<string, Author>().ForMember(dest => dest.Name, opts => opts.MapFrom(src => src));
cfg.CreateMap<BookViewModel, Book>()
.ForMember(dest => dest.Author, opts => opts.MapFrom(src => src.Author));
});
方法2:
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<Book, BookViewModel>()
.ForMember(dest => dest.Author, opts => opts.MapFrom(src => src.Author.Name));
cfg.CreateMap<BookViewModel, Book>()
.ForMember(dest => dest.Author, opts => opts.MapFrom(src => new Author {Name = src.Author}));
});
然后调用就可以了:
var book = Mapper.Map<BookViewModel, Book>(model);
参考:
1、https://blog.csdn.net/hetoby/article/details/77623569
2、https://www.cnblogs.com/fred-bao/p/5700776.html