AutoMapper小结

摘要:
对于某些orm框架,当使用Entity时,一些开源代码使用automapper将数据对象转换为DTO。在创建过程中,如果我们手动执行,我们将看到以下代码:Bb=newB();b、 XXX1=a.XXX1;b、 XXX2=a.XXX2;。。。。。。。。。返回b;此时,AutoMapper可以根据A的模型和B的模型的定义,自动将模型A映射到全新的模型B。人们认为自动映射器是为了使数据库实体到外部调用实体的转换更容易。使用AutoMapper实现此转换。
一些orm框架,在用到Entity的时候有一些开源代码用到了automapper(如:nopcommence),将数据对象转成DTO。比如在ORM中,与数据库交互用的Model模型是具有很多属性变量方法神马的。而当我们与其它系统(或系统中的其它结构)进行数据交互时,出于耦合性考虑或者安全性考虑或者性能考虑(总之就是各种考虑),我们不希望直接将这个Model模型传递给它们,这时我们会创建一个贫血模型来保存数据并传递。神马是贫血模型?贫血模型(DTO,Data Transfer Object)就是说只包含属性神马的,只能保存必须的数据,木有其它任何的多余的方法数据什么的,专门用于数据传递用的类型对象。在这个创建的过程中,如果我们手动来进行,就会看到这样的代码:
B b=new B();
b.XXX1=a.XXX1;
b.XXX2=a.XXX2;
...
...
...
return b;
此时,AutoMapper可以发挥的作用就是根据A的模型和B的模型中的定义,自动将A模型映射为一个全新的B模型。基于访问性的控制或从模型本身上考虑。对外开放的原则是,尽量降低系统耦合度,否则内部一旦变更外部所有的接口都要跟随发生变更;另外,系统内部的一些数据或方法并不希望外部能看到或调用。类似的考虑很多,只是举个例子。系统设计的原则是高内聚低耦合,尽量依赖抽象而不依赖于具体。这里感觉automapper就是使数据库实体对一个外部调用实体的转换更简便(不用一个属性一个属性的赋值)。
例如1:数据库里面有用户信息表,供别的系统调用,提供了数据接口。如果直接暴露了数据库层的表结构的话,会对系统本身产生依赖。具体表现在,假定现在因为某种需要,为用户信息增加了十个字段的信息,那么,如果不进行类型映射的话,会导致所有基于此用户数据结构的模块集体挂掉(接口约定变更)。而如果使用了映射的话,我们可以在内部进行转换,保持原有接口不变并提供新的更全面的接口,这是保证系统的可维护性和可迁移性。

例如2:一个Web应用通过前端收集用户的输入成为Dto,然后将Dto转换成领域模型并持久化到数据库中。相反,当用户请求数据时,我们又需要做相反的工作:将从数据库中查询出来的领域模型以相反的方式转换成Dto再呈现给用户。使用AutoMapper(一个强大的Object-Object Mapping工具),来实现这个转换。
【一】 应用场景

 先来看看我所”虚拟“的领域模型。这一次我定义了一个书店(BookStore): 

  1. public class BookStore  
  2. {  
  3.     public string Name { get; set; }  
  4.     public List<Book> Books { get; set; }  
  5.     public Address Address { get; set; }  
  6. }  


书店有自己的地址(Address): 

  1. public class Address  
  2. {  
  3.     public string Country { get; set; }  
  4.     public string City { get; set; }  
  5.     public string Street { get; set; }  
  6.     public string PostCode { get; set; }  
  7. }  


同时书店里放了n本书(Book): 

  1. public class Book  
  2. {  
  3.     public string Title { get; set; }  
  4.     public string Description { get; set; }  
  5.     public string Language { get; set; }  
  6.     public decimal Price { get; set; }  
  7.     public List<Author> Authors { get; set; }  
  8.     public DateTime? PublishDate { get; set; }  
  9.     public Publisher Publisher { get; set; }  
  10.     public int? Paperback { get; set; }  
  11. }  


每本书都有出版商信息(Publisher): 

  1. public class Publisher  
  2. {  
  3.     public string Name { get; set; }  
  4. }  


每本书可以有最多2个作者的信息(Author): 

  1. public class Author  
  2. {  
  3.     public string Name { get; set; }  
  4.     public string Description { get; set; }  
  5.     public ContactInfo ContactInfo { get; set; }  
  6. }  

每个作者都有自己的联系方式(ContactInfo): 

  1. public class ContactInfo  
  2. {  
  3.     public string Email { get; set; }  
  4.     public string Blog { get; set; }  
  5.     public string Twitter { get; set; }  
  6. }  

差不多就是这样了,一个有着层级结构的领域模型。 
再来看看我们的Dto结构。 
在Dto中我们有与BookStore对应的BookStoreDto: 

  1. public class BookStoreDto  
  2. {  
  3.     public string Name { get; set; }  
  4.     public List<BookDto> Books { get; set; }  
  5.     public AddressDto Address { get; set; }  
  6. }  

其中包含与Address对应的AddressDto: 

  1. public class AddressDto  
  2. {  
  3.     public string Country { get; set; }  
  4.     public string City { get; set; }  
  5.     public string Street { get; set; }  
  6.     public string PostCode { get; set; }  
  7. }  

以及与Book相对应的BookDto: 

  1. public class BookDto  
  2. {  
  3.     public string Title { get; set; }  
  4.     public string Description { get; set; }  
  5.     public string Language { get; set; }  
  6.     public decimal Price { get; set; }  
  7.     public DateTime? PublishDate { get; set; }  
  8.     public string Publisher { get; set; }  
  9.     public int? Paperback { get; set; }  
  10.     public string FirstAuthorName { get; set; }  
  11.     public string FirstAuthorDescription { get; set; }  
  12.     public string FirstAuthorEmail { get; set; }  
  13.     public string FirstAuthorBlog { get; set; }  
  14.     public string FirstAuthorTwitter { get; set; }  
  15.     public string SecondAuthorName { get; set; }  
  16.     public string SecondAuthorDescription { get; set; }  
  17.     public string SecondAuthorEmail { get; set; }  
  18.     public string SecondAuthorBlog { get; set; }  
  19.     public string SecondAuthorTwitter { get; set; }  
  20. }  

注意到我们的BookDto”拉平了“整个Book的层级结构,一个BookDto里携带了Book及其所有Author、Publisher等所有模式的数据。

正好我们来看一下Dto到Model的映射规则。 
(1)BookStoreDto –> BookStore

BookStoreDto中的字段BookStore中的字段
NameName
BooksBooks
AddressAddress

(2)AddressDto –> Address

AddressDto中的字段Address中的字段
CountryCountry
CityCity
StreetStreet
PostCodePostCode

(3)BookDto -> Book。 
BookDto中的一些基本字段可以直接对应到Book中的字段。

BookDto中的字段Book中的字段
TitleTitle
DescriptionDescription
LanguageLanguage
PricePrice
PublishDatePublishDate
PaperbackPaperback

每本书至多有2个作者,在BookDto中分别使用”First“前缀和”Second“前缀的字段来表示。因此,所有FirstXXX字段都将映射成Book的Authors中的第1个Author对象,而所有SecondXXX字段则将映射成Authors中的第2个Author对象。

BookDto中的字段Book中的Authors中的第1个Author对象中的字段
FirstAuthorNameName
FirstAuthorDescriptionDescription
FirstAuthorEmailContactInfo.Email
FirstAuthorBlogContactInfo.Blog
FirstAuthorTwitterContactInfo.Twitter

注意上表中的ContactInfo.Email表示对应到Author对象的ContactInfo的Email字段,依次类推。类似的我们有:

BookDto中的字段Book中的Authors中的第2个Author对象中的字段
SecondAuthorNameName
SecondAuthorDescriptionDescription
SecondAuthorEmailContactInfo.Email
SecondAuthorBlogContactInfo.Blog
SecondAuthorTwitterContactInfo.Twitter

最后还有Publisher字段,它将对应到一个独立的Publisher对象。

BookDto中的字段Publisher中的字段
PublisherName

差不多就是这样了,我们的需求是要实现这一大坨Dto到另一大坨的Model之间的数据转换。

【二】以Convention方式实现零配置的对象映射

在上一篇文章中我们构造出了完整的应用场景,包括我们的Model、Dto以及它们之间的转换规则。下面就可以卷起袖子,开始我们的AutoMapper之旅了。

引用
AutoMapper uses a convention-based matching algorithm to match up source to destination values.


我们要做的只是将要映射的两个类型告诉AutoMapper(调用Mapper类的Static方法CreateMap并传入要映射的类型): 

  1. Mapper.CreateMap<AddressDto, Address>();  

然后就可以交给AutoMapper帮我们搞定一切了: 

  1. AddressDto dto = new AddressDto  
  2. {  
  3.     Country = "China",  
  4.     City = "Beijing",  
  5.     Street = "Dongzhimen Street",  
  6.     PostCode = "100001"  
  7. };  
  8. Address address = Mapper.Map<AddressDto,Address>(Dto);  
  9. address.Country.ShouldEqual("China");  
  10. address.City.ShouldEqual("Beijing");  
  11. address.Street.ShouldEqual("Dongzhimen Street");  
  12. address.PostCode.ShouldEqual("100001");  

如果AddressDto中有值为空的属性,AutoMapper在映射的时候会把Address中的相应属性也置为空: 

  1. Address address = Mapper.Map<AddressDto,Address>(new AddressDto  
  2.                                                        {  
  3.                                                            Country = "China"  
  4.                                                        });  
  5. address.City.ShouldBeNull();  
  6. address.Street.ShouldBeNull();  
  7. address.PostCode.ShouldBeNull();  

甚至如果传入一个空的AddressDto,AutoMapper也会帮我们得到一个空的Address对象。 

  1. Address address = Mapper.Map<AddressDto,Address>(null);  
  2. address.ShouldBeNull();  

千万不要把这种Convention的映射方式当成“玩具”,它在映射具有相同字段名的复杂类型的时候还是具有相当大的威力的。 
例如,考虑我们的BookStoreDto到BookStore的映射,两者的字段名称完全相同,只是字段的类型不一致。如果我们定义好了BookDto到Book的映射规则,再加上上述Convention方式的AddressDto到Address的映射,就可以用“零配置”实现BookStoreDto到BookStore的映射了: 

  1. IMappingExpression<BookDto, Book> expression = Mapper.CreateMap<BookDto,Book>();  
  2. // Define mapping rules from BookDto to Book here  
  3. Mapper.CreateMap<AddressDto, Address>();  
  4. Mapper.CreateMap<BookStoreDto, BookStore>();  

然后我们就可以直接转换BookStoreDto了: 

  1. BookStoreDto dto = new BookStoreDto  
  2.                        {  
  3.                            Name = "My Store",  
  4.                            Address = new AddressDto  
  5.                                          {  
  6.                                              City = "Beijing"  
  7.                                          },  
  8.                            Books = new List<BookDto>  
  9.                                        {  
  10.                                            new BookDto {Title = "RESTful Web Service"},  
  11.                                            new BookDto {Title = "Ruby for Rails"},  
  12.                                        }  
  13.                        };  
  14. BookStore bookStore = Mapper.Map<BookStoreDto,BookStore>(dto);  
  15. bookStore.Name.ShouldEqual("My Store");  
  16. bookStore.Address.City.ShouldEqual("Beijing");  
  17. bookStore.Books.Count.ShouldEqual(2);  
  18. bookStore.Books.First().Title.ShouldEqual("RESTful Web Service");  
  19. bookStore.Books.Last().Title.ShouldEqual("Ruby for Rails");  

实现BookDto到Book之间的转换(他嵌套了相应的子类型如:Publisher ->ContactInfo,Author):

  1. var exp = Mapper.CreateMap<BookDto, Book>();
  2. exp.ForMember(bok=> bok.Publisher/*(变量)*/,
  3. (map) => map.MapFrom(dto=>new Publisher(){Name= dto.Publisher/*(DTO的变量)*/}));
一般在我们写完规则之后通常会调用
  1. //该方法主要用来检查还有那些规则没有写完。 Mapper.AssertConfigurationIsValid();

参见:http://stackoverflow.com/questions/4928487/how-to-automap-thismapping-sub-members

其它的就以此类推。

如果要完成 BookStore 到 BookStoreDto 具体的应该如何映射呢 
相同的类型与名字就不说了,如BookStore.Name->BookStoreDto.Name AutoMapper会自动去找。

而对于List<Book>与List<BookDto>者我们必须在配置下面代码之前

  1. var exp = Mapper.CreateMap<BookStore, BookStoreDto>();
  2. exp.ForMember(dto => dto.Books, (map) => map.MapFrom(m => m.Books));

告诉AutoMapper,Book与BookDto的映射,最后效果为:

  1. Mapper.CreateMap<Book, BookDto>();
  2. var exp = Mapper.CreateMap<BookStore, BookStoreDto>();
  3. exp.ForMember(dto => dto.Books, (map) => map.MapFrom(m => m.Books));

Address同理。

如果要完成不同类型之间的转换用AutoMapper,如string到int,string->DateTime,以及A->B之间的类型转换我们可以参照如下例子:

http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters&referringTitle=Home

对于我们不想要某属性有值我们可以采用下面的方式。

exp.ForMember(ads => ads.ZipCode, dto => dto.Ignore()); //如果对于不想某属性有值,我们可以通过Ignore来忽略他,这样在调用AssertConfigurationIsValid时也不会报错.

【三】定义类型间的简单映射规则 
前面我们看了Convention的映射方式,客观的说还是有很多类型间的映射是无法通过简单的Convention方式来做的,这时候就需要我们使用Configuration了。好在我们的Configuration是在代码中以“强类型”的方式来写的,比写繁琐易错的xml方式是要好的多了。 
先来看看BookDto到Publisher的映射。 
回顾一下前文中定义的规则:BookDto.Publisher -> Publisher.Name。 
在AutoMapperzhong,我们可以这样映射: 

  1. var map = Mapper.CreateMap<BookDto,Publisher>();  
  2. map.ForMember(d => d.Name, opt => opt.MapFrom(s => s.Publisher));  

AutoMapper使用ForMember来指定每一个字段的映射规则: 

引用
The each custom member configuration uses an action delegate to configure each member.


还好有强大的lambda表达式,规则的定义简单明了。 

此外,我们还可以使用ConstructUsing的方式一次直接定义好所有字段的映射规则。例如我们要定义BookDto到第一作者(Author)的ContactInfo的映射,使用ConstructUsing方式,我们可以: 

  1. var map = Mapper.CreateMap<BookDto,ContactInfo>();  
  2. map.ConstructUsing(s => new ContactInfo  
  3.                                           {  
  4.                                               Blog = s.FirstAuthorBlog,  
  5.                                               Email = s.FirstAuthorEmail,  
  6.                                               Twitter = s.FirstAuthorTwitter  
  7.                                           });  

然后,就可以按照我们熟悉的方式来使用了: 

  1. BookDto dto = new BookDto  
  2.                         {  
  3.                             FirstAuthorEmail = "matt.rogen@abc.com",  
  4.                             FirstAuthorBlog = "matt.amazon.com",  
  5.                         };  
  6. ContactInfo contactInfo = Mapper.Map<BookDto, ContactInfo>(dto);  

如果需要映射的2个类型有部分字段名称相同,又有部分字段名称不同呢?还好AutoMapper给我们提供的Convention或Configuration方式并不是“异或的”,我们可以结合使用两种方式,为名称不同的字段配置映射规则,而对于名称相同的字段则忽略配置。 
例如对于前面提到的AddressDto到Address的映射,假如AddressDto的字段Country不叫Country叫CountryName,那么在写AddressDto到Address的映射规则时,只需要: 

  1. var map = Mapper.CreateMap<AddressDto, Address>();  
  2. map.ForMember(d => d.Country, opt => opt.MapFrom(s => s.CountryName));  

对于City、Street和PostCode无需定义任何规则,AutoMapper仍然可以帮我们进行正确的映射。

例如2 转载自:http://zz8ss5ww6.iteye.com/blog/1126330

 
转自:http://www.cnblogs.com/jobs2/p/3503990.html

免责声明:文章转载自《AutoMapper小结》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vue项目动态新增表单、图片、文件Python解析Pcap包类源码学习下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

springJpa 自定义参数查询

创建表结构 跟上一章一样,我们还是使用商品信息表、商品类型表来完成编码。 商品信息表 -- ---------------------------- -- Table structure for good_infos -- ---------------------------- DROP TABLE IF EXISTS `good_infos`;...

lvm空间扩容

打算给系统装一个oracle,发现磁盘空间不足。在安装系统的时候我选择的是自动分区,系统就会自动以LVM的方式分区。为了保证系统后期的可用性,建议所有新系统安装都采用LVM,之后生产上的设备我也打算这样做。 参于LVM基本介绍与常用命令请参考:http://www.cnblogs.com/jackruicao/p/6258812.html LVM结构图 扩...

分布式中的DTO(转)

简介 数据传输对象(DTO),是一种设计模式之间传输数据的软件应用系统。数据传输目标往往是结合使用的数据访问对象从数据库中检索数据。   数据传输对象与数据交互对象或数据访问对象之间的差异是一个以不具有任何行为除了存储和检索的数据(访问和存取器)。   在传统的系统(企业JavaBeans)体系结构,数据传输目标服务的双重目的:第一,他们围 绕这个问题,p...

springboot使用druid连接池连接Oracle数据库的基本配置

#阿里连接池配置 #spring.datasource.druid.driver-class-name=oracle.jdbc.driver.OracleDriver #可配可不配,阿里的数据库连接池会通过url自动搜寻 spring.datasource.druid.url=jdbc:oracle:thin:@localhost:1521:orcl...

.Net Core + DDD基础分层 + 项目基本框架 + 个人总结

为什么要写这篇文章 1,在大半年前,公司开发任务比较轻松,自己不知道干什么,但是又想要学习技术,比较迷茫,后面我接触到了博客园,看了一个帖子,深有感触,我当时不知道学习什么,于是我信息给他,他居然回复我了,并且还给我建议学习的方向,以及学习的方法,我十分的感谢他。 2,在一次面试中,有人问我,你工作1年多了有没有做过自我总结,你觉得你的优势是什么,我当时吞...

Auto Mapper02《demo》

学习这些基本上网上都有一些教程或者别人做的demo,我是按照这个方式去学习的。先做个demo,学会如何去使用它,接着去慢慢的了解它是如何的运行的,理解里面的一些基本的基础知识。我们不可以再像学校里面的那样,先去学习基本的结构,那样我们会很烦脑的,我们应该先学会使用它,接着再去慢慢的了解。 “高内聚低耦合,尽量依赖抽象而不依赖于具体”,这个在面向对象的开发中...