Dot-Net

嵌套映射的 AutoMapper 展平需要自定義解析器

  • April 11, 2017

我對 AutoMapper 有點陌生,想將 POCO-ish 對象映射到可能更複雜的 DTO,後者試圖代表Google Books API 的 Volume資源:

圖書.cs

public class Book
{
   public string Isbn10 { get; set; }
   public string Isbn13 { get; set; }
   public string Title { get; set; }
   public string Author { get; set; }
   public string Publisher { get; set; }
   public DateTime Publication { get; set; }
   public int Pages { get; set; }
   public string Description { get; set; }
   public bool InStock { get; set; }
}

BookDto.cs

public class BookDto
{
   public string Kind { get; set; }
   public string Id { get; set; }
   public VolumeInfo VolumeInfo { get; set; }
}

public class VolumeInfo
{
   public string Title { get; set; }
   public List<string> Authors { get; set; }
   public string Publisher { get; set; }
   public string PublishedDate { get; set; }
   public string Description { get; set; }
   public int PageCount { get; set; }
   public List<IndustryIdentifier> IndustryIdentifiers { get; set; }
}

public class IndustryIdentifier
{
   public string Type { get; set; }
   public string Identifier { get; set; }
}

因此,根據文件 ,我們可以簡單地展平嵌套類型:

AutoMapperConfigurator.cs

public static class AutoMapperConfigurator
{
   public static void Configure()
   {
       Mapper.CreateMap<Book, BookDto>()
           .ForMember(dto => dto.Id, options => options.Ignore())
           .ForMember(dto => dto.Kind, options => options.Ignore())
           .ForMember(dto => dto.VolumeInfo.Title, options => options.MapFrom(book => book.Title))
           .ForMember(dto => dto.VolumeInfo.Authors, options => options.MapFrom(book => book.Author.ToArray()))
           .ForMember(dto => dto.VolumeInfo.Publisher, options => options.MapFrom(book => book.Publisher))
           .ForMember(dto => dto.VolumeInfo.PublishedDate, options => options.MapFrom(book => book.Publication))
           .ForMember(dto => dto.VolumeInfo.Description, options => options.MapFrom(book => book.Description))
           .ForMember(dto => dto.VolumeInfo.PageCount, options => options.MapFrom(book => book.Pages))
           ;
   }
}

但不幸的是,在執行Mapper.AssertConfigurationIsValid()測試時出現以下錯誤:

System.ArgumentException:表達式 ‘dto => dto.VolumeInfo.Title’ 必須解析為頂級成員,而不是任何子對象的屬性。在子類型或 AfterMap 選項上使用自定義解析器。參數名稱:lambdaExpression

所以現在按照這個建議嘗試使用 AfterMap:

public static class AutoMapperConfigurator
{
   public static void Configure()
   {
       Mapper.CreateMap<Book, BookDto>()
           .ForMember(dto => dto.Id, options => options.Ignore())
           .ForMember(dto => dto.Kind, options => options.Ignore())
           .AfterMap((book, bookDto) => bookDto.VolumeInfo = new VolumeInfo 
               { 
                   Title = book.Title,
                   Authors = new List<string>(){ book.Author },
                   Publisher = book.Publisher,
                   PublishedDate = book.Publication.ToShortDateString(),
                   Description = book.Description,
                   PageCount = book.Pages
               });
   }
}

再次執行測試時,我現在收到此消息:

未映射的成員被發現。查看下面的類型和成員。添加自定義映射表達式,忽略,添加自定義解析器,或修改源/目標類型 Book -> BookDto(目標成員列表) Dotnet.Samples.AutoMapper.Book -> Dotnet.Samples.AutoMapper.BookDto(目標成員列表)音量資訊

我應該在嵌套類之間創建額外的映射嗎?任何指導將不勝感激,在此先感謝。

在將 .ForMember 與 VolumnInfo 映射的內部映射一起使用之前,我已經做了類似的事情:

public static class AutoMapperConfigurator
{
   public static void Configure()
   {
       Mapper.CreateMap<Book, VolumeInfo>()
           .ForMember(dto => dto.Authors, options => options.MapFrom(book => book.Author.Split(',')))
           .ForMember(dto => dto.PublishedDate, options => options.MapFrom(book => book.Publication))
           .ForMember(dto => dto.PageCount, options => options.MapFrom(book => book.Pages))
           .ForMember(dto => dto.IndustryIdentifiers, options => options.Ignore());

       Mapper.CreateMap<Book, BookDto>()
           .ForMember(dto => dto.Id, options => options.Ignore())
           .ForMember(dto => dto.Kind, options => options.Ignore())
           .ForMember(dto => dto.VolumeInfo, options => options.MapFrom(book => Mapper.Map<Book, VolumeInfo>(book)));
   }
}

以下是一些驗證功能的單元測試:

[TestFixture]
public class MappingTests
{
   [Test]
   public void AutoMapper_Configuration_IsValid()
   {
       AutoMapperConfigurator.Configure();
       Mapper.AssertConfigurationIsValid();
   }

   [Test]
   public void AutoMapper_MapsAsExpected()
   {
       AutoMapperConfigurator.Configure();
       Mapper.AssertConfigurationIsValid();

       var book = new Book
           {
               Author = "Castle,Rocks",
               Description = "Awesome TV",
               InStock = true,
               Isbn10 = "0123456789",
               Isbn13 = "0123456789012",
               Pages = 321321,
               Publication = new DateTime(2012, 11, 01),
               Publisher = "Unknown",
               Title = "Why I Rock"
           };

       var dto = Mapper.Map<Book, BookDto>(book);

       Assert.That(dto.Id, Is.Null);
       Assert.That(dto.Kind, Is.Null);
       Assert.That(dto.VolumeInfo, Is.Not.Null);
       Assert.That(dto.VolumeInfo.Authors, Is.Not.Null);
       Assert.That(dto.VolumeInfo.Authors.Count, Is.EqualTo(2));
       Assert.That(dto.VolumeInfo.Authors[0], Is.EqualTo("Castle"));
       Assert.That(dto.VolumeInfo.Authors[1], Is.EqualTo("Rocks"));
       Assert.That(dto.VolumeInfo.Description, Is.EqualTo("Awesome TV"));
       Assert.That(dto.VolumeInfo.IndustryIdentifiers, Is.Null);
       Assert.That(dto.VolumeInfo.PageCount, Is.EqualTo(321321));
       Assert.That(dto.VolumeInfo.PublishedDate, Is.EqualTo(new DateTime(2012, 11, 01).ToString()));
       Assert.That(dto.VolumeInfo.Publisher, Is.EqualTo("Unknown"));
       Assert.That(dto.VolumeInfo.Title, Is.EqualTo("Why I Rock"));
   }
}

引用自:https://stackoverflow.com/questions/13334938