AutoMapper目錄:
本篇目錄:
隨著AutoMapper的學習深入,發現AutoMapper在對象轉換方面(Object-Object Mapping)還蠻強大的,當時使用AutoMapper的場景是DTO與Domin Model相互轉換,所以文章的標題就是這個(標題有誤),其實AutoMapper不止在這方面的轉換,應該是涵蓋所有對象(Object)之間的轉換,上篇主要講AutoMapper的基本轉換使用,本篇可以定義為AutoMapper的靈活配置篇。
插一句:有時候學習一門技術,還沒有學很深入,發現還有比其更好的,然后就去學習另外一門技術,可能到頭來什么也沒學會、學精,前兩天看一篇C#程序員-你為何不受大公司青睞,其實不是C#不好,而是你沒有學好,就像前幾年討論C#和java哪個好一樣,我個人覺得去爭論這些很是無聊,還不如靜下心,好好的學好一門技術,那就是成功的。
在上篇中,我們使用AutoMapper類型映射轉換,都是相同的類型轉換,比如string轉string、datetime轉datetime,但是有時候在一些復雜的業務場景中,可能會存在跨類型映射轉換,比如我們下面的場景:
1 public class Source 2 { 3 public string Value1 { get; set; } 4 public string Value2 { get; set; } 5 public string Value3 { get; set; } 6 } 7 public class Destination 8 { 9 public int Value1 { get; set; }10 public DateTime Value2 { get; set; }11 public Type Value3 { get; set; }12 }從代碼中可以看出,string需要轉換為目標類型:int、DateTime和Type,轉換的類型并不一致,雖然我們命名符合PascalCase命名規則,但是如果還是按照正常的類型映射轉換就會報下面異常:

”AutoMapperMappingException“這個異常我們在上篇中提到多次,AutoMapper在類型映射的時候,如果找不到或映射類型不一致都會報這個異常,怎么辦?天無絕人之路,AutoMapper提供了自定義類型轉換器:
1 // 2 // 摘要: 3 // Skip member mapping and use a custom type converter instance to convert to 4 // the destination type 5 // 6 // 類型參數: 7 // TTypeConverter: 8 // Type converter type 9 void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;10 //11 // 摘要:12 // Skip member mapping and use a custom function to convert to the destination13 // type14 //15 // 參數:16 // mappingFunction:17 // Callback to convert from source type to destination type18 void ConvertUsing(Func<TSource, TDestination> mappingFunction);19 //20 // 摘要:21 // Skip member mapping and use a custom type converter instance to convert to22 // the destination type23 //24 // 參數:25 // converter:26 // Type converter instance27 void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
ConvertUsing是什么?它是我們在指定類型映射時,類型配置的方法,就是說通過ConvertUsing方法把類型映射中類型轉換的權限交給用戶配置,而不是通過AutoMapper進行自動類型轉換,這就給我提供了更多的自定義性,也就避免了不同類型之間轉換而引起的”AutoMapperMappingException“異常。
ConvertUsing三個重載方法,第二個適用于簡單類型轉換,接受一個類型,返回一個目標類型,第一個和第三個其實基本一樣,一個是實例,一個是類型,但都必須是ITypeConverter<TSource, TDestination>泛型接口的派生類。
正好上面示例中需要對三種類型進行轉換,就分別用ConvertUsing三個重載方法,因為string轉int很簡單就使用第二個重載方法,string轉DateTime、Type自定義類型轉換器:
1 public class DateTimeTypeConverter : TypeConverter<string, DateTime> 2 { 3 PRotected override DateTime ConvertCore(string source) 4 { 5 return System.Convert.ToDateTime(source); 6 } 7 } 8 public class TypeTypeConverter : TypeConverter<string, Type> 9 {10 protected override Type ConvertCore(string source)11 {12 TypeConverter dd = TypeDescriptor.GetConverter(source.GetType());13 dd.CanConvertTo(typeof(Type));14 Type type = Assembly.GetExecutingAssembly().GetType(source);15 return type;16 }17 }當然業務場景如果復雜的話,可以在轉換器中做些詳細的配置內容,TypeConverter的CanConvertTo方法是判斷相互轉換類型的可行性,不可轉換返回false,除此之外,再列下TypeConverter的幾個常用方法:
| CanConvertFrom(Type) | 返回該轉換器是否可以將給定類型的對象轉換為此轉換器的類型。 |
| CanConvertFrom(ITypeDescriptorContext, Type) | 返回該轉換器是否可以使用指定的上下文將給定類型的對象轉換為此轉換器的類型。 |
| CanConvertTo(Type) | 返回此轉換器是否可將該對象轉換為指定的類型。 |
| CanConvertTo(ITypeDescriptorContext, Type) | 返回此轉換器是否可以使用指定的上下文將該對象轉換為指定的類型。 |
| ConvertFrom(Object) | 將給定值轉換為此轉換器的類型。 |
| ConvertFrom(ITypeDescriptorContext, CultureInfo, Object) | 使用指定的上下文和區域性信息將給定的對象轉換為此轉換器的類型。 |
AutoMapper配置轉換代碼:
1 public void Example() 2 { 3 var source = new Source 4 { 5 Value1 = "5", 6 Value2 = "01/01/2000", 7 Value3 = "DTO_AutoMapper使用詳解.GlobalTypeConverters+Destination" 8 }; 9 10 // 配置 AutoMapper11 Mapper.CreateMap<string, int>().ConvertUsing(Convert.ToInt32);12 Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());13 Mapper.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>();14 Mapper.CreateMap<Source, Destination>();15 Mapper.AssertConfigurationIsValid();16 17 // 執行 mapping18 Destination result = Mapper.Map<Source, Destination>(source);19 Console.WriteLine("result.Value1:" + result.Value1.ToString());20 Console.WriteLine("result.Value2:" + result.Value2.ToString());21 Console.WriteLine("result.Value3:" + result.Value3.ToString());22 }在自定義轉換配置中雖然配置了轉換類型,但是CreateMap中也需要制定其類型,而且要和轉換器中類型所一致,最后Mapper.CreateMap<Source, Destination>();完成Source到Destination的配置轉換,其實上面的配置器可以看成Source(原始類型)和Destination(目標類型)所依賴類型之間的轉換。轉換效果:

自定義轉換配置器的強大之處在于,我們可以完成任何類型之間的相互轉換(只要符合CanConvertTo),因為類型轉換我們說了算,在業務場景中,我們可以定義一組自定義轉換配置器,這樣就不需要再做額外的配置,就可以完成想要的類型轉換。
上面講了自定義類型轉換器,針對的是不同類型之間映射處理,有這樣一種場景:領域模型到DTO的轉換,DTO并不是和領域模型之間完全一樣,而且還要根據具體的業務場景做一些處理,什么意思?比如我們要對DTO做一些測試或其他一些數據操作(如記錄日志時間等),但是和業務無關,如果把這種操作放在領域模型中就有點不倫不類了,所以要在DTO轉換中去做,比如下面場景:
1 public class Source2 {3 public int Value1 { get; set; }4 public int Value2 { get; set; }5 }6 public class Destination7 {8 public int Total { get; set; }9 }轉換目標對象中我們想得到一個計算值,就是在轉換中對目標值進行解析,如果你看了Projection這一節點,可能覺得很簡單,我們可以使用自定義轉換規則就可以做到:
1 Mapper.CreateMap<Source, Destination>()2 .ForMember(dest => dest.Total, opt => opt.MapFrom(src => src.Value1 + src.Value2));
這種方式雖然可以解決上述場景中的問題,但是不提倡這樣做,如果解析過程復雜一些,或者解析方式經常出現改動,這樣我們維護起來就很麻煩了,所以我們要定義一個值解析器,或者稱為目標值解析器,和上面說的類型轉換器(ConvertUsing)比較類似,AutoMapper提供了ResolveUsing方法用于目標值解析器:
1 // 2 // 摘要: 3 // Resolve destination member using a custom value resolver 4 // 5 // 類型參數: 6 // TValueResolver: 7 // Value resolver type 8 // 9 // 返回結果:10 // Value resolver configuration options11 IResolverConfigurationExpression<TSource, TValueResolver> ResolveUsing<TValueResolver>() where TValueResolver : IValueResolver;12 //13 // 摘要:14 // Resolve destination member using a custom value resolver callback. Used instead15 // of MapFrom when not simply redirecting a source member access both the source16 // object and current resolution context for additional mapping, context items17 // and parent objects This method cannot be used in conjunction with LINQ query18 // projection19 //20 // 參數:21 // resolver:22 // Callback function to resolve against source type23 void ResolveUsing(Func<ResolutionResult, object> resolv
新聞熱點
疑難解答