AutoMapper Dto complete conversion and Model

In the actual software development projects, our "business logic" we often need various changes to the same data.

For example, a Web application user input collected by the front end becomes Dto, and then converted into domain model Dto and persisted to the database. On the contrary, when the user requests data, we need to do the reverse: the domain model from the database query out into Dto then presented to the user in the opposite way.

Sometimes we will be faced with more data needs, for example, the use of multiple data clients, each client has different needs for data structures, and this requires us to do more data conversion. 
Frequent data conversion trivial but messy, many times we had to do: 
(1) in two types pretty much just different names and structures are generally similar, but the data can only be achieved by way of the manual, one by one property assignment between types of "transfer". 
(2) confronted with each corner of the data conversion operation is repeated but dispersed in a new application data conversion scene is a conversion logic performed manually, lead. 
If there is such a "Transformers" -like tool, the "orange" We want to become the "Apple", and we need to do is define good transformation rules - do we really business logic, or even in a simple scene under the rules do not even need to define (Convention Over Configuration), it will be a very good thing. In fact, in .NET, we do not reinvent the wheel, because we have --AutoMapper, a powerful Object-Object Mapping tool. 
Okay, I admit that I have a little bit excited, I did in fact project is experiencing more "confusion", while AutoMapper really gives me the feeling shines. So I took a little weekend break try a little AutoMapper, mapping the domain model to achieve Dto through a small application scenarios, we do feel its "powerful aura." I will share their experiences in the use of the article, I hope to give you the same is in the puzzlement bring a little help. I complete the project code will be released at a later time to own git repository, a welcome to the free use and reference.

[A] will convert Dto Model

Let's look at my domain model "virtual". This time I define a bookstore (BookStore):

      public class BookStore
      {
        public string Name { get; set; }
         public Address Address { get; set; }
     }

 

Bookstore has its own address (Address):

     public class Address
   {
        public string Country { get; set; }
        public string City { get; set; }
        public string Street { get; set; }
         public string PostCode { get; set; }
   }

 

 

Meanwhile bookstores put n the book (Book):

       public class Book
      {
         public string Title { get; set; }
         public string Description { get; set; }
         public string Language { get; set; }
         public decimal Price { get; set; }
         public List<Author> Authors { get; set; }
         public DateTime? PublishDate { get; set; }
         public Publisher Publisher { get; set; }
         public int? Paperback { get; set; }
      }

 

Each book has a publisher of information (Publisher):

       public class Publisher
        {
            public string Name { get; set; }
        }

 

There may be two of the most information (Author) each book:

   public class Author
    {
        public string Name { get; set; }
         public string Description { get; set; }
         public ContactInfo ContactInfo { get; set; }
     }

 

Each author has his own contact details (ContactInfo):

      public class ContactInfo
         {
            public string Email { get; set; }
          public string Blog { get; set; }
          public string Twitter { get; set; }
       }

 

Almost is the case, a domain model with a hierarchy. 
Let's look at our Dto structure. 
In Dto we have with BookStore corresponding BookStoreDto:

    public class BookStoreDto
      {
         public string Name { get; set; }
         public List<BookDto> Books { get; set; }
          public AddressDto Address { get; set; }
      }

 

Address which contains corresponding AddressDto:

       public class AddressDto
       {
          public string Country { get; set; }
           public string City { get; set; }
        public string Street { get; set; }
           public string PostCode { get; set; }
      }

 

Book well as corresponding BookDto:

      public class BookDto
        {
           public string Title { get; set; }
          public string Description { get; set; }
            public string Language { get; set; }
          public decimal Price { get; set; }
          public DateTime? PublishDate { get; set; }
           public string Publisher { get; set; }
          public int? Paperback { get; set; }
          public string FirstAuthorName { get; set; }
         public string FirstAuthorDescription { get; set; }
        public string FirstAuthorEmail { get; set; }
           public string FirstAuthorBlog { get; set; }
         public string FirstAuthorTwitter { get; set; }
          public string SecondAuthorName { get; set; }
           public string SecondAuthorDescription { get; set; }
         public string SecondAuthorEmail { get; set; }
           public string SecondAuthorBlog { get; set; }
          public string SecondAuthorTwitter { get; set; }
       }

 

We noticed that our BookDto "leveled" the entire Book of hierarchy, a BookDto carry data in all modes and all of Book Author, Publisher and so on. 
We just look at Dto to the mapping rules Model. 
(1) BookStoreDto -> BookStore

BookStoreDto in the field BookStore in the field
Name Name
Books Books
Address Address

(2)AddressDto –> Address

AddressDto in the field Address in the field
Country Country
City City
Street Street
PostCode PostCode

(3) BookDto -> Book. 
BookDto Some basic fields may correspond directly to the fields Book.

BookDto in the field Book of the field
Title Title
Description Description
Language Language
Price Price
PublishDate PublishDate
Paperback Paperback

 

Each book has at most two authors, use the "First" prefix and "Second" prefix in BookDto the field are represented. Therefore, all fields are mapped to FirstXXX Authors Book of the 1st Author object, and then mapped to all SecondXXX fields in the second Authors Author object.

BookDto in the field Book of the 1st Authors Author object field
FirstAuthorName Name
FirstAuthorDescription Description
FirstAuthorEmail ContactInfo.Email
FirstAuthorBlog ContactInfo.Blog
FirstAuthorTwitter ContactInfo.Twitter

Note that the table ContactInfo.Email indicate corresponding to the Author object ContactInfo Email Address field, and so on. Similarly, we have:

BookDto in the field Book of Authors of the second field in the Author object
SecondAuthorName Name
SecondAuthorDescription Description
SecondAuthorEmail ContactInfo.Email
SecondAuthorBlog ContactInfo.Blog
SecondAuthorTwitter ContactInfo.Twitter

Finally, there is Publisher field, it corresponds to a separate Publisher objects.

BookDto in the field Publisher Fields in
Publisher Name

Almost is the case, we need to realize this big lump Dto to data transfer between another large lump of Model.

[Two] to convert Dto Model

1,以Convention方式实现零配置的对象映射

我们的AddressDto和Address结构完全一致,且字段名也完全相同。对于这样的类型转换,AutoMapper为我们提供了Convention,正如它的官网上所说的:

引用

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

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

C#代码

    Mapper.CreateMap<AddressDto, Address>(); 

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

            AddressDto dto = new AddressDto
              {
                  Country = "China",
                  City = "Beijing",
                  Street = "Dongzhimen Street",
                  PostCode = "100001"
              };
              Address address = Mapper.Map<AddressDto,Address>(Dto);
              address.Country.ShouldEqual("China");
              address.City.ShouldEqual("Beijing");
              address.Street.ShouldEqual("Dongzhimen Street");
              address.PostCode.ShouldEqual("100001");

 

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

              Address address = Mapper.Map<AddressDto,Address>(new AddressDto
                                                                     {
                                                                         Country = "China"
                                                                     });
              address.City.ShouldBeNull();
              address.Street.ShouldBeNull();
              address.PostCode.ShouldBeNull();

 

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

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

 

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

C#代码

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

 

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

        BookStoreDto dto = new BookStoreDto
                                     {
                                         Name = "My Store",
                                         Address = new AddressDto
                                                       {
                                                           City = "Beijing"
                                                       },
                                         Books = new List<BookDto>
                                                     {
                                                         new BookDto {Title = "RESTful Web Service"},
                                                         new BookDto {Title = "Ruby for Rails"},
                                                     }
                                     };
              BookStore bookStore = Mapper.Map<BookStoreDto,BookStore>(dto);
              bookStore.Name.ShouldEqual("My Store");
              bookStore.Address.City.ShouldEqual("Beijing");
              bookStore.Books.Count.ShouldEqual(2);
              bookStore.Books.First().Title.ShouldEqual("RESTful Web Service");
              bookStore.Books.Last().Title.ShouldEqual("Ruby for Rails");

 

 

以下为自己补充:

(Begin)---------------------------------------------------------------

1, 要实现BookDto到Book之间的转换还是有一断路需要走的因为他嵌套了相应的子类型如:Publisher ->ContactInfo,

Author。废话少说,直接上答案:

  var exp = Mapper.CreateMap<BookDto, Book>(); 
 exp.ForMember(bok=> bok.Publisher/*(变量)*/,
   (map) => map.MapFrom(dto=>new Publisher(){Name= dto.Publisher/*(DTO的变量)*/}));

 

 
一般在我们写完规则之后通常会调用
 
         //该方法主要用来检查还有那些规则没有写完。
          Mapper.AssertConfigurationIsValid();

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

其它的就以此类推。

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

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

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

 

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

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

 

Address同理。

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

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

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

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

 

 

(End)------------------------------------------------------------------------------------

 

【三】定义类型间的简单映射规则

前面我们看了Convention的映射方式,客观的说还是有很多类型间的映射是无法通过简单的Convention方式来做的,这时候就需要我们使用Configuration了。好在我们的Configuration是在代码中以“强类型”的方式来写的,比写繁琐易错的xml方式是要好的多了。 
先来看看BookDto到Publisher的映射。

回顾一下前面中定义的规则:BookDto.Publisher -> Publisher.Name。

在AutoMapperzhong,我们可以这样映射:

 var map = Mapper.CreateMap<BookDto,Publisher>();
 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方式,我们可以:

C#代码

   var map = Mapper.CreateMap<BookDto,ContactInfo>();
   map.ConstructUsing(s => new ContactInfo
                                             {
                                                 Blog = s.FirstAuthorBlog,
                                                 Email = s.FirstAuthorEmail,
                                                 Twitter = s.FirstAuthorTwitter
                                             });

 

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

            BookDto dto = new BookDto
                                     {
                                         FirstAuthorEmail = "[email protected]",
                                         FirstAuthorBlog = "matt.amazon.com",
                                     };
             ContactInfo contactInfo = Mapper.Map<BookDto, ContactInfo>(dto);

 

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

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

 

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

该文转自:http://zz8ss5ww6.iteye.com/blog/1126219

Guess you like

Origin www.cnblogs.com/zhurunlai/p/11118430.html
dto