AutoMapper(六)

tkbSimplest發表於2015-11-07

返回總目錄


List和陣列

AutoMapper只要求元素型別的配置而不要求可能會用到的任何陣列或者list型別。比如,我們有一個簡單的源和目標型別:

public class Source
{
    public int Value { get; set; }
}

public class Destination
{
    public int Value { get; set; }
}

支援所有的基本泛型集合,程式碼如下:

class Program
{
    static void Main(string[] args)
    {
        Mapper.CreateMap<Source, Destination>();
        var sources = new[]
        {
            new Source() {Value = 1},
            new Source() {Value = 2},
            new Source() {Value = 3},
        };
        IEnumerable<Destination> iEnumerableDests= Mapper.Map<IEnumerable<Destination>>(sources);
        ICollection<Destination> iCollectionDests= Mapper.Map<ICollection<Destination>>(sources);
        IList<Destination> iListDests= Mapper.Map<IList<Destination>>(sources);
        List<Destination> listDests= Mapper.Map<List<Destination>>(sources);
        Destination[] destsArr= Mapper.Map<Destination[]>(sources);
        //這裡只舉兩個例子,其他集合同理
        foreach (var dest in iCollectionDests)
        {
            Console.Write(dest.Value+",");
        }
        Console.WriteLine();
        foreach (var dest in destsArr)
        {
            Console.Write(dest.Value + ",");
        }


        Console.Read();
    }
}

以上程式碼是集合和集合之間的對映,但是對映的配置CreateMap方法中只是配置的是型別之間的對映,而沒有設計任何集合型別。

測試結果如下,可見集合之間對映成功:

image

具體來說,支援的源集合型別包括:

  • IEnumerable
  • IEnumerable<T>
  • ICollection
  • ICollection<T>
  • IList
  • IList<T>
  • List<T>
  • Arrays

集合中的多型元素型別

很多時候,在我們的源和目標型別中可能有一個型別層次關係。AutoMapper支援多型陣列和集合,因此如果發現派生的源或者目標型別,就會使用它們。

public class ParentSource
{
    public int Value1 { get; set; }
}

public class ChildSource : ParentSource
{
    public int Value2 { get; set; }
}

public class ParentDestination
{
    public int Value1 { get; set; }
}

public class ChildDestination : ParentDestination
{
    public int Value2 { get; set; }
}

AutoMapper仍然要求顯示配置孩子對映,因為它不能“猜出”具體使用哪一個孩子目標對映。

在Main方法中新增如下程式碼:

Mapper.Initialize(c =>
{
    c.CreateMap<ParentSource, ParentDestination>()
        .Include<ChildSource, ChildDestination>();
    c.CreateMap<ChildSource, ChildDestination>();
});
var sources = new[]
{
    new ParentSource(){Value1 = 11},
    new ChildSource(){Value2 = 22},
    new ParentSource(),
};

var dests = Mapper.Map<ParentDestination[]>(sources);
Console.WriteLine(dests[0]);
Console.WriteLine(dests[1]);
Console.WriteLine(dests[2]);

測試結果如下:

image

上面我們建立了一個源的陣列,其中包含兩個ParentSource和一個ChildSource,所以兩個ParentSource成功地對映到了ParentDestination,而CreateMap配置中,ParentSource到ParentDestination的對映配置包含了ChildSource到ChildDestination的配置,所以執行Mapper.Map<ParentDestination[]>(sources)的時候,也可以將ChildSource對映到ChildDestination。

對映繼承

在派生類中標明繼承

上面的程式碼,是在基類中配置繼承的,除此之外,也可以在派生類中配置繼承:

//在基類中配置繼承
 Mapper.Initialize(c =>
 {
     c.CreateMap<ParentSource, ParentDestination>()
         .Include<ChildSource, ChildDestination>();
     c.CreateMap<ChildSource, ChildDestination>();
 });
 //在派生類中配置繼承
 Mapper.Initialize(c =>
 {
     c.CreateMap<ParentSource, ParentDestination>();
     c.CreateMap<ChildSource, ChildDestination>()
         .IncludeBase<ParentSource, ParentDestination>();
 });

繼承對映屬性

這裡介紹一下額外的複雜性,因為一個屬性對映時可以有多種方式。下面是這些源的優先順序:

  • 顯式對映 (使用.MapFrom())
  • 繼承的顯式對映 
  • 慣例對映 (通過慣例匹配的屬性)
  • 忽略的屬性對映

下面來演示一下:

這裡還是用上面定義的四個類:Order,OrderDto,PCOrder,MobileOrder:

//領域物件
public class Order { }
//電腦端訂單
public class PCOrder : Order
{
    public string Referrer { get; set; }
}
//手機訂單
public class MobileOrder : Order { }

//Dtos
public class OrderDto
{
    public string Referrer { get; set; }
}

配置對映的方法使用的是在父類中配置繼承對映

//在父類中配置繼承對映
Mapper.CreateMap<Order, OrderDto>()
    .Include<PCOrder,OrderDto>()
    .Include<MobileOrder,OrderDto>()
    .ForMember(o => o.Referrer, m => m.Ignore());//這裡配置了忽略目標屬性Referrer的對映
Mapper.CreateMap<PCOrder,OrderDto>();
Mapper.CreateMap<MobileOrder, OrderDto>();
// 執行對映
var order = new PCOrder() { Referrer = "天貓" };
var mapped = Mapper.Map<OrderDto>(order);
Console.WriteLine(mapped.Referrer);

執行結果如下:

image

注意在我們的對映配置中,我們已經忽略了Referrer(因為Order基類中不存在這個屬性),但是在基類的對映中,慣例比忽略的屬性有更高的優先順序,因而屬性仍然得到了對映。

相關文章