AutoMapper的匹配
1,智能匹配
AutoMapper能夠自動識別和匹配大部分對象屬性:
-
- 如果源類和目標類的屬性名稱相同,直接匹配,不區分大小寫
-
- 目標類型的CustomerName可以匹配源類型的Customer.Name
-
- 目標類型的Total可以匹配源類型的GetTotal()方法
2,自定義匹配
Mapper.CreateMap<CalendarEvent, CalendarEventForm>() //屬性匹配,匹配源類中WorkEvent.Date到EventDate
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.WorkEvent.Date))
.ForMember(dest => dest.SomeValue, opt => opt.Ignore()) //忽略目標類中的屬性
.ForMember(dest => dest.TotalAmount, opt => opt.MapFrom(src => src.TotalAmount ?? 0)) //復雜的匹配
.ForMember(dest => dest.OrderDate, opt => opt.UserValue<DateTime>(DateTime.Now)); //固定值匹配
直接匹配
源類的屬性名稱和目標類的屬性名稱相同(不區分大小寫),直接匹配,Mapper.CreateMap<source,dest>();無需做其他處理,此處不再細述
Flattening
將一個復雜的對象模型拉伸為,或者扁平化為一個簡單的對象模型,如下面這個復雜的對象模型:
public class Order { private readonly IList<OrderLineItem> _orderLineItems = new List<OrderLineItem>(); public Customer Customer { get; set; } public OrderLineItem[] GetOrderLineItems() { return _orderLineItems.ToArray(); } public void AddOrderLineItem(Product product, int quantity) { _orderLineItems.Add(new OrderLineItem(product, quantity)); } public decimal GetTotal() { return _orderLineItems.Sum(li => li.GetTotal()); } } public class Product { public decimal Price { get; set; } public string Name { get; set; } } public class OrderLineItem { public OrderLineItem(Product product, int quantity) { Product = product; Quantity = quantity; } public Product Product { get; private set; } public int Quantity { get; private set; } public decimal GetTotal() { return Quantity * Product.Price; } } public class Customer { public string Name { get; set; } }
我們要把這一復雜的對象簡化為OrderDTO,只包含某一場景所需要的數據:
public class OrderDto { public string CustomerName { get; set; } public decimal Total { get; set; } }
運用AutoMapper轉換:
public void Example() { // Complex model var customer = new Customer { Name = "George Costanza" }; var order = new Order { Customer = customer }; var bosco = new Product { Name = "Bosco", Price = 4.99m }; order.AddOrderLineItem(bosco, 15); // Configure AutoMapper var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>()); // Perform mapping var mapper = config.CreateMapper(); OrderDto dto = mapper.Map<Order, OrderDto>(order); dto.CustomerName.ShouldEqual("George Costanza"); dto.Total.ShouldEqual(74.85m); }
可以看到只要設置下Order和OrderDto之間的類型映射就可以了,我們看OrderDto中的CustomerName和Total屬性在領域模型Order中並沒有與之相對性,AutoMapper在做解析的時候會按照PascalCase(帕斯卡命名法),CustomerName其實是由Customer+Name 得來的,是AutoMapper的一種映射規則;而Total是因為在Order中有GetTotal()方法,AutoMapper會解析“Get”之后的單詞,所以會與Total對應。在編寫代碼過程中可以運用這種規則來定義名稱實現自動轉換。
Projection
Projection可以理解為與Flattening相反,Projection是將源對象映射到一個不完全與源對象匹配的目標對象,需要制定自定義成員,如下面的源對象:
public class CalendarEvent { public DateTime EventDate { get; set; } public string Title { get; set; } }
目標對象:
public class CalendarEventForm { public DateTime EventDate { get; set; } public int EventHour { get; set; } public int EventMinute { get; set; } public string Title { get; set; } }
AutoMapper配置轉換代碼:
public void Example() { // Model var calendarEvent = new CalendarEvent { EventDate = new DateTime(2008, 12, 15, 20, 30, 0), Title = "Company Holiday Party" }; var config = new MapperConfiguration(cfg => { // Configure AutoMapper cfg.CreateMap<CalendarEvent, CalendarEventForm>() .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date)) .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour)) .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute)); }); // Perform mapping var mapper = config.CreateMapper(); CalendarEventForm form = mapper.Map<CalendarEvent, CalendarEventForm>(calendarEvent); form.EventDate.ShouldEqual(new DateTime(2008, 12, 15)); form.EventHour.ShouldEqual(20); form.EventMinute.ShouldEqual(30); form.Title.ShouldEqual("Company Holiday Party"); }
Configuration Validation
在進行對象映射的時候,有可能會出現屬性名稱或者自定義匹配規則不正確而又沒有發現的情況,在程序執行時就報錯,因此,AutoMapper提供的AssertConfigurationIsValid()方法來驗證結構映射是否正確。
config.AssertConfigurationIsValid();如果映射錯誤,會報“AutoMapperConfigurationException”異常錯誤,就可以進行調試修改了
Lists and Array
AutoMapper支持的源集合類型包括:
- IEnumerable
- IEnumerable<T>
- ICollection
- ICollection<T>
- IList
- IList<T>
- List<T>
- Arrays
有一種情況是,在使用集合類型類型的時候,類型之間存在繼承關系,例如下面我們需要轉換的類型:
//源對象 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需要孩子映射的顯式配置,AutoMapper配置轉換代碼:
var config = new MapperConfiguration(cfg => { cfg.CreateMap<ParentSource, ParentDestination>() .Include<ChildSource, ChildDestination>(); cfg.CreateMap<ChildSource, ChildDestination>(); }); var sources = new[] { new ParentSource(), new ChildSource(), new ParentSource() }; var destinations = config.CreateMapper().Map<ParentSource[], ParentDestination[]>(sources); destinations[0].ShouldBeType<ParentDestination>(); destinations[1].ShouldBeType<ChildDestination>(); destinations[2].ShouldBeType<ParentDestination>();
注意在Initialize初始化CreateMap進行類型映射配置的時候有個Include泛型方法,簽名為:“Include this configuration in derived types' maps”,大致意思為包含派生類型中配置,ChildSource是ParentSource的派生類,ChildDestination是ParentDestination的派生類,cfg.CreateMap<ParentSource, ParentDestination>().Include<ChildSource, ChildDestination>(); 這段代碼是說明ParentSource和ChildSource之間存在的關系,並且要要顯示的配置。
Nested mappings
嵌套對象映射,例如下面的對象:
public class OuterSource { public int Value { get; set; } public InnerSource Inner { get; set; } } public class InnerSource { public int OtherValue { get; set; } } //目標對象 public class OuterDest { public int Value { get; set; } public InnerDest Inner { get; set; } } public class InnerDest { public int OtherValue { get; set; } }
AutoMapper配置轉換代碼:
var config = new MapperConfiguration(cfg => { cfg.CreateMap<OuterSource, OuterDest>(); cfg.CreateMap<InnerSource, InnerDest>(); }); config.AssertConfigurationIsValid(); var source = new OuterSource { Value = 5, Inner = new InnerSource {OtherValue = 15} }; var dest = config.CreateMapper().Map<OuterSource, OuterDest>(source); dest.Value.ShouldEqual(5); dest.Inner.ShouldNotBeNull(); dest.Inner.OtherValue.ShouldEqual(15);
對於嵌套映射,只要指定下類型映射關系和嵌套類型映射關系就可以了,也就是這段代碼:“Mapper.CreateMap<InnerSource, InnerDest>();” 其實我們在驗證類型映射的時候加上Mapper.AssertConfigurationIsValid(); 這段代碼看是不是拋出“AutoMapperMappingException”異常來判斷類型映射是否正確。
參考資料
關於AutoMapper,陸續更新中...