自定義型別轉換
有時,需要完全控制一個型別到另一個型別的轉換。一個型別一點都不像另一個型別,而且轉換函式已經存在了,在這種情況下,你想要從一個“寬鬆”的型別轉換成一個更強壯的型別,例如一個string的源型別到一個int32的目標型別。
這裡有兩個類Source和Destination,要把前者對映到後者,程式碼如下:
public class Source { public string Value1 { get; set; } public string Value2 { get; set; } public string Value3 { get; set; } }
public class Destination { public int Value1 { get; set; } public DateTime Value2 { get; set; } public Type Value3 { get; set; } }
截至發稿前,官方文件這樣描述的“因為AutoMapper不清楚從string到int,Datetime或Type的對映,如果要嘗試對映的話,AutoMapper就會丟擲異常(在對映時和配置檢查時)”。為了建立 這些型別的對映,我們必須提供自定義的型別轉換器,我們有三種方法這樣做:
void ConvertUsing(Func<TSource, TDestination> mappingFunction); void ConvertUsing(ITypeConverter<TSource, TDestination> converter); void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;
但是,我先不實現自定義的型別轉換器試一下:
namespace ThirdAutoMapper { class Program { static void Main(string[] args) { Mapper.CreateMap<Source, Destination>(); var source = new Source { Value1 = "5", Value2 = "05/11/2015", Value3 = "ThirdAutoMapper.Source" }; var destination = Mapper.Map<Destination>(source); Console.WriteLine("destination.Value1={0}", destination.Value1); Console.WriteLine("destination.Value2={0}", destination.Value2); Console.Read(); } } }
出現如下錯誤,但從錯誤看來,只有從string到Type型別對映時,出現了錯誤,其他兩種型別呢?
現在我們將源型別和目標型別的第三個屬性Value3註釋掉,同時註釋掉參與對映前的源型別物件的Value3屬性,再來試一下。
果然,發現對映成功。也就說說最新版的AutoMapper預設可以將string型別對映為int和DateTime型別了。但是string還是沒辦法對映為Type型別。
對於以上AutoMapper的API給出的三種轉換方法,第一個選擇只是輸入一個源返回一個目標的函式,這對於簡單場合是有效的,但是對於更大的案例會變得難以處理。在更復雜的情況下,我們可以一個自定義的型別轉換器,通過實現ITypeConverter<TSource, TDestination>介面建立:
第一種方法:
public class CustomTypeConverter : ITypeConverter<Source, Destination> { public Destination Convert(ResolutionContext context) { Source src = context.SourceValue as Source; var dest = new Destination { Value1 = System.Convert.ToInt32(src.Value1), Value2 = System.Convert.ToDateTime(src.Value2), Value3 = context.SourceType }; return dest; } }
然後給AutoMapper提供一個自定義型別的轉換器或者AutoMapper在執行時能例項化的簡化型別。對於上面的源/目標型別的對映配置就得以實現了:
修改Main方法中的程式碼為:
static void Main(string[] args) { Mapper.CreateMap<Source, Destination>().ConvertUsing<CustomTypeConverter>(); var source = new Source { Value1 = "5", Value2 = "05/11/2015", Value3 = typeof(Source).ToString()
}; var destination = Mapper.Map<Destination>(source); Console.WriteLine("destination.Value1={0}", destination.Value1); Console.WriteLine("destination.Value2={0}", destination.Value2); Console.WriteLine(destination.Value3.ToString()==source.Value3); Console.WriteLine(destination.Value3); Console.Read(); }
測試結果如下,對映成功!
第二種方法:
每個型別分別實現ITypeConverter介面:
public class DateTimeTypeConverter:ITypeConverter<string,DateTime> { public DateTime Convert(ResolutionContext context) { return System.Convert.ToDateTime(context.SourceValue); } } public class TypeTypeConverter : ITypeConverter<string, Type> { public Type Convert(ResolutionContext context) { return context.SourceValue == typeof(Source).ToString() ? typeof(Source) : typeof(Destination); } }
static void Main(string[] args) { //我看網上很多這種寫法,包括官方文件,但是我這樣寫編譯錯誤,可能是現在的AutoMapper不支援了吧 // Mapper.CreateMap<string, int>().ConvertUsing( Convert.ToInt32); Mapper.CreateMap<string, int>().ConvertUsing((context, intNum) => Convert.ToInt32(context.SourceValue)); Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter().Convert); Mapper.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>(); Mapper.CreateMap<Source, Destination>(); //Mapper.CreateMap<Source, Destination>().ConvertUsing<CustomTypeConverter>(); var source = new Source { Value1 = "5", Value2 = "05/11/2015", Value3 = typeof(Source).ToString() }; var destination = Mapper.Map<Destination>(source); Console.WriteLine("destination.Value1={0}", destination.Value1); Console.WriteLine("destination.Value2={0}", destination.Value2); Console.WriteLine(destination.Value3.ToString()==source.Value3); Console.WriteLine(destination.Value3); Console.Read(); }
測試結果,同樣完美通過。