AutoMapper用法(轉載)

申明

本文轉載自http://www.qeefee.com/article/automapperapp

做者:齊飛ide

配置AutoMapper映射規則

AutoMapper是基於約定的,所以在實用映射以前,咱們須要先進行映射規則的配置。函數

 1 public class Source
 2 {
 3     public int SomeValue { get; set; }
 4     public string AnotherValue { get; set; }
 5 }
 6 
 7 public class Destination
 8 {
 9     public int SomeValue { get; set; }
10 }

 在上面的代碼中,咱們定義了兩個類,咱們須要將Source類的對象映射到Destination類的對象上面。要完成這個操做,咱們須要對AutoMapper進行以下配置:測試

1 Mapper.CreateMap<Source, Destination>();

進行一下測試:ui

1 Source src = new Source() { SomeValue = 1, AnotherValue = "2" };
2 Destination dest = Mapper.Map<Destination>(src);
3 ObjectDumper.Write(dest);

咱們能夠在控制檯看到dest對象的屬性值:spa

這樣咱們就完成了一個簡單的AutoMapper映射。3d

Profile的用法

Profile提供了一個命名的映射類,全部繼承自Profile類的子類都是一個映射集合。code

咱們來看一下Profile的用法,這個例子中仍然使用上面的Source類和Destination類。orm

1 public class SourceProfile : Profile
2 {
3     protected override void Configure()
4     {
5         CreateMap<Source, Destination>();
6     }
7 }

咱們能夠再Profile中重寫Configure方法,從而完成映射規則的配置。從Profile初始化Mapper規則:對象

1 Mapper.Initialize(x => x.AddProfile<SourceProfile>());

在一個Profile中,咱們能夠完成多個、更復雜的規則的約定:

 1 public class Destination2
 2 {
 3     public int SomeValue { get; set; }
 4     public string AnotherValue2 { get; set; }
 5 }
 6 
 7 public class SourceProfile : Profile
 8 {
 9     protected override void Configure()
10     {
11         //Source->Destination
12         CreateMap<Source, Destination>();
13 
14         //Source->Destination2
15         CreateMap<Source, Destination2>().ForMember(d => d.AnotherValue2, opt =>
16         {
17             opt.MapFrom(s => s.AnotherValue);
18         });
19     }
20 }

AutoMapper最佳實踐

這段內容將討論AutoMapper的規則寫在什麼地方的問題。

在上一段中,咱們已經知道了如何使用AutoMapper進行簡單的對象映射,可是,在實際的項目中,咱們會有不少類進行映射(從Entity轉換爲Dto,或者從Entity轉換爲ViewModel等),這麼多的映射如何組織將成爲一個問題。

首先咱們須要定義一個Configuration.cs的類,該類提供AutoMapper規則配置的入口,它只提供一個靜態的方法,在程序第一次運行的時候調用該方法完成配置。

當有多個Profile的時候,咱們能夠這樣添加:

 1 public class Configuration
 2 {
 3     public static void Configure()
 4     {
 5         Mapper.Initialize(cfg =>
 6         {
 7             cfg.AddProfile<Profiles.SourceProfile>();
 8             cfg.AddProfile<Profiles.OrderProfile>();
 9             cfg.AddProfile<Profiles.CalendarEventProfile>();
10         });
11     }
12 }

在程序運行的時候,只須要調用Configure方法便可。

瞭解了這些實現之後,咱們能夠再項目中添加AutoMapper文件夾,文件夾結構以下:

Configuration爲咱們的靜態配置入口類;Profiles文件夾爲咱們全部Profile類的文件夾。若是是MVC,咱們須要在Global中調用:

1 AutoMapper.Configuration.Configure();

扁平化映射(Flattening)

默認狀況下,咱們的Source類和Destination類是根據屬性名稱進行匹配映射的。除此以外,默認的映射規則還有下面兩種狀況,咱們稱之爲扁平化映射,即當Source類中不包含Destination類中的屬性的時候,AutoMapper會將Destination類中的屬性進行分割,或匹配「Get」開頭的方法,例如:

Order類:

1 public class Order
2 {
3     public Customer Customer { get; set; }
4 
5     public decimal GetTotal()
6     {
7         return 100M;
8     }
9 }

Order類中包含了一個customer對象和一個GetTotal方法,爲了方便演示,我直接將GetTotal方法返回100;

Customer類的定義以下:

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

OrderDto類的定義以下:

1 public class OrderDto
2 {
3     public string CustomerName { get; set; }
4     public string Total { get; set; }
5 }

咱們在進行映射的時候,不須要進行特殊的配置,既能夠完成從Order到OrderDto的映射。

1 public class OrderProfile : Profile
2 {
3     protected override void Configure()
4     {
5         CreateMap<Entity.Order, Dto.OrderDto>();
6     }
7 }

測試代碼:

1 Entity.Customer customer = new Entity.Customer() { Name = "Tom" };
2 Entity.Order order = new Entity.Order() { Customer = customer };
3 Dto.OrderDto orderDto = Mapper.Map<Dto.OrderDto>(order);
4 ObjectDumper.Write(order, 2);
5 ObjectDumper.Write(orderDto);

測試結果:

指定映射字段(Projection)

在實際的業務環境中,咱們的Source類和Destination類的字段不可能一對一的匹配,這個時候咱們就須要來指定他們的實際映射關係,例如:

 1 public class CalendarEvent
 2 {
 3     public DateTime Date { get; set; }
 4     public string Title { get; set; }
 5 }
 6 
 7 public class CalendarEventForm
 8 {
 9     public DateTime EventDate { get; set; }
10     public int EventHour { get; set; }
11     public int EventMinute { get; set; }
12     public string DisplayTitle { get; set; }
13 }

在這兩個類中,CalendarEvent的Date將被拆分爲CalendarEventForm的日期、時、分三個字段,Title也將對應DisplayTitle字段,那麼相應的Profile定義以下:

 1 public class CalendarEventProfile : Profile
 2 {
 3     protected override void Configure()
 4     {
 5         CreateMap<Entity.CalendarEvent, Entity.CalendarEventForm>()
 6             .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))
 7             .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.Date.Hour))
 8             .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.Date.Minute))
 9             .ForMember(dest => dest.DisplayTitle, opt => opt.MapFrom(src => src.Title));
10     }
11 }

測試代碼:

1 Entity.CalendarEvent calendarEvent = new Entity.CalendarEvent()
2 {
3     Date = DateTime.Now,
4     Title = "Demo Event"
5 };
6 Entity.CalendarEventForm calendarEventForm = Mapper.Map<Entity.CalendarEventForm>(calendarEvent);
7 ObjectDumper.Write(calendarEventForm);

測試結果:

驗證配置項(Configuration Validation)

AutoMapper提供了一種驗證機制,用來判斷Destination類中的全部屬性是否都被映射,若是存在未被映射的屬性,則拋出異常。

驗證的用法:

1 Mapper.AssertConfigurationIsValid();

例如:

1 public class Source
2 {
3     public int SomeValue { get; set; }
4     public string AnotherValue { get; set; }
5 }

Destination代碼:

1 public class Destination
2 {
3     public int SomeValuefff { get; set; }
4 }

測試:

1 Mapper.CreateMap<Entity.Source, Entity.Destination>();
2 Mapper.AssertConfigurationIsValid();

運行程序將會出現AutoMapperConfigurationException異常:

這是由於SomeValuefff在Source類中沒有對應的字段形成的。

解決這種異常的方法有:

指定映射字段,例如:

1 Mapper.CreateMap<Entity.Source, Entity.Destination>()
2     .ForMember(dest => dest.SomeValuefff, opt =>
3     {
4         opt.MapFrom(src => src.SomeValue);
5     });

或者使用Ignore方法:

1 Mapper.CreateMap<Entity.Source, Entity.Destination>()
2     .ForMember(dest => dest.SomeValuefff, opt =>
3     {
4         opt.Ignore();
5     });

或者使用自定義解析器,自定義解析器在下面講到。

自定義解析器(Custom value resolvers)

AutoMapper容許咱們自定義解析器來完成Source到Destination的值的轉換。例如:

 1 public class Source
 2 {
 3     public int Value1 { get; set; }
 4     public int Value2 { get; set; }
 5 }
 6 
 7 public class Destination
 8 {
 9     public int Total { get; set; }
10 }

Total屬性在Source中不存在,若是如今建立映射規則,在映射的時候必然會拋出異常。這個時候咱們就須要使用自定義解析器來完成映射。

自定義解析器須要實現 IValueResolver 接口,接口的定義以下:

1 public interface IValueResolver
2 {
3     ResolutionResult Resolve(ResolutionResult source);
4 }

咱們來自定義一個Resolver:

1 public class CustomResolver : ValueResolver<Source, int>
2 {
3     protected override int ResolveCore(Source source)
4     {
5         return source.Value1 + source.Value2;
6     }
7 }

而後在映射規則中使用這個解析器:

 1 public class SourceProfile : Profile
 2 {
 3     protected override void Configure()
 4     {
 5         //Source->Destination
 6         CreateMap<Source, Destination>()
 7             .ForMember(dest => dest.Total, opt =>
 8             {
 9                 opt.ResolveUsing<CustomResolver>();
10             });
11     }
12 }

測試代碼:

1 Source src = new Source()
2 {
3     Value1 = 1,
4     Value2 = 2
5 };
6 Destination dest = Mapper.Map<Destination>(src);
7 ObjectDumper.Write(dest);

測試結果:

在使用自定義Resolver中,咱們還能夠指定Resolver的構造函數,例如:

1 //Source->Destination
2 CreateMap<Source, Destination>()
3     .ForMember(dest => dest.Total, opt =>
4     {
5         opt.ResolveUsing<CustomResolver>()
6             .ConstructedBy(() => new CustomResolver());
7     });

自定義類型轉換器(Custom type converters)

 AutoMapper經過ConvertUsing來使用自定義類型轉換器。ConvertUsing有三種用法:

1 void ConvertUsing(Func<TSource, TDestination> mappingFunction);
2 void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
3 void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;

當咱們有以下的Source類和Destination類:

1 public class Source
2 {
3     public string Value1 { get; set; }
4 }
5 
6 public class Destination
7 {
8     public int Value1 { get; set; }
9 }

咱們可使用以下配置:

 1 public class SourceProfile : Profile
 2 {
 3     protected override void Configure()
 4     {
 5         //string->int
 6         CreateMap<string, int>()
 7             .ConvertUsing(Convert.ToInt32);
 8         //Source->Destination
 9         CreateMap<Source, Destination>();
10     }
11 }

在上面的配置中,咱們首先建立了從string到int的類型轉換,這裏使用了系統自帶的Convert.ToInt32轉換方法。

除了這種方法以外,咱們還能夠自定義類型轉換器:

 1 public class CustomConverter : ITypeConverter<Source, Destination>
 2 {
 3     public Destination Convert(ResolutionContext context)
 4     {
 5         Source src = context.SourceValue as Source;
 6         Destination dest = new Destination();
 7         dest.Value1 = System.Convert.ToInt32(src.Value1);
 8 
 9         return dest;
10     }
11 }

經過這個轉換器,咱們能夠繞過string到int的轉換,直接將Source類的對象轉換爲Destination類的對象。

對應的配置以下:

1 public class SourceProfile : Profile
2 {
3     protected override void Configure()
4     {
5         //Source->Destination
6         CreateMap<Source, Destination>()
7             .ConvertUsing<CustomConverter>();
8     }
9 }

或者,咱們也可使用下面的配置:

 1 public class SourceProfile : Profile
 2 {
 3     protected override void Configure()
 4     {
 5         //Source->Destination
 6         CustomConverter converter = new CustomConverter();
 7         CreateMap<Source, Destination>()
 8             .ConvertUsing(converter);
 9     }
10 }

空值替換(Null substitution)

空值替換容許咱們將Source對象中的空值在轉換爲Destination的值的時候,使用指定的值來替換空值。

1 public class Source
2 {
3     public string Value { get; set; }
4 }
5 
6 public class Destination
7 {
8     public string Value { get; set; }
9 }

配置代碼:

 1 public class SourceProfile : Profile
 2 {
 3     protected override void Configure()
 4     {
 5         //Source->Destination
 6         CreateMap<Source, Destination>()
 7             .ForMember(dest => dest.Value, opt =>
 8             {
 9                 opt.NullSubstitute("原始值爲NULL");
10             });
11     }
12 }

測試代碼:

1 Source src = new Source();
2 Destination dest = Mapper.Map<Destination>(src);
3 ObjectDumper.Write(dest);

測試結果:

條件映射(Conditional mapping)

條件映射只當Source類中的屬性值知足必定條件的時候才進行映射。例如:

1 public class Foo
2 {
3     public int baz;
4 }
5 
6 public class Bar
7 {
8     public uint baz;
9 }

對應的配置代碼以下:

1 Mapper.CreateMap<Foo, Bar>()
2     .ForMember(dest => dest.baz, opt =>
3     {
4         opt.Condition(src => (src.baz >= 0));
5     }); 

轉載

做者:齊飛

原文:http://www.qeefee.com/article/automapper

相關文章
相關標籤/搜索