AutoMapper是一個對象和對象間的映射器。對象與對象的映射是通過轉變一種類型的輸入對象為一種不同類型的輸出對象工作的。讓AutoMapper有意思的地方在於它提供了一些將類型A映射到類型B這種無聊的事情的有趣慣例。只要類型B遵守AutoMapper已經建立的慣例,大多數情況下,映射兩種類型零配置就可以了。
首先,需要有source(源)和destination(目標)類型。默認情況下AutoMapper 只要Destination類型的成員名字與Source類型的成員名字匹配(成員名稱不區分大小寫),並且成員類型相同就直接會將值傳遞給Destination類型。
AutoMapper只要做一次初始化就可以使用,因此最好的配置代碼的地方應該在應用啟動時。
初始化處理(Profile實例)
Initialize():實例化Mapper對象
CreateMap():創建映射關系 Ex:Mapper.Initialize(cfg=>{ cfg.CreateMap<Source, Dest>(); });
AddProfile:使用配置文件創建映射關系 Ex: Mapper.Initialize(cfg=>{cfg.AddProfile<MyProfile>(); });對於MyProfile對象需要繼承Profile對象,並重寫 Configure()方法。
映射前后操作:
BeforeMap(): 在映射前執行的處理。Ex:BeforeMap((src, dest) => src.Value = src.Value + 1)
AfterMap(): 在映射后執行的處理。 Ex:AfterMap((src, dest) => src.Name = "FengTest")
條件映射
ForMember():對於映射中成員的處理。
Condition():對於條件映射處理,滿足該條件才會給目標成員賦值。
Ex:ForMember(dest => dest.Value, opt => opt.Condition(src => src.Value > 0 && src.Value < 130));
命名慣例
SourceMemberNamingConvention :源類型成員命名規則
Ex: cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); //下划線命名法
cfg.DestinationMemberNamingConvention :目標類型成員命名規則
Ex: cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention(); //帕斯卡命名法
替換字符
ReplaceMemberName():將源成員名稱中的字符替換成新的字符此方法必須在CreateMap之前
Ex: cfg.ReplaceMemberName("Tool", "Car");
前后綴
RecognizePrefixes():識別源成員名稱中的前綴字符 Ex: cfg.RecognizePrefixes("P");
RecognizePostfixes():識別源成員名稱中的后綴字符 Ex: cfg.RecognizePostfixes("L");
此方法必須在CreateMap之前,並且默認是識別“Get”字符前綴
ClearPrefixes(): 清除前綴 Ex:cfg.CliearPrefixes()
全局過慮
ShouldMapField : 字段的映射條件 Ex: cfg.ShouldMapField = field => false; //不映射任何字段
ShouldMapProperty:屬性的映射條件
Ex:cfg.ShouldMapProperty = pro => pro.GetMethod != null && pro.GetMethod.IsPrivate;
此方法必須是源類型與目標類型都滿足該條件才能映射處理,默認情況下AutoMapper只對公共成員做映射,如果想識別Private/internal,可以修改這兩個屬性值。
構造函數
默認情況下,當目標類型的構造函數中的參數名稱、類型與源類型成員的名稱、類型相同,AutoMapper會自動映射。
當構造函數的參數名稱與目標成員的名稱不同時,可使用ForCtorParam()方法指定。
ForCtorParam() : 對構造函數參數映射Ex:ForCtorParam("age1",user=>user.MapFrom(src=>src.Age))
條件對象映射器
AddConditionalObjectMapper():符合條件后兩個類型自動映射。
Ex:cfg.AddConditionalObjectMapper().Where((s, d) => d.Name == s.Name + "Dto");
成員配置
AddMemberConfiguration():配置文件, 配置中的東西都是以該方法開始的。
Ex:cfg.AddMemberConfiguration().AddMember<NameSplitMember>(); 默認配置
AddName<ReplaceName>(r => r.AddReplace("B", "A")) 替換字符
AddName<PrePostfixName>(p => p.AddStrings(pr => pr.Prefixes, "Get", "get"))識別前綴
AddName<PrePostfixName>(p => p.AddStrings(pr => pr.Postfixes, "Set", "set"))識別后綴
AddName<SourceToDestinationNameMapperAttributesMember>() 特性支持
[MapToAttribute("Phone")] :特性,匹配基於給定名稱的屬性
AutoMapper默認值:AddMemberConfiguration().AddMember<NameSplitMember>()
.AddName<PrePostfixName>(_ => _.AddStrings(p => p.Prefixes,"Get"))
.AddName<SourceToDestinationNameMapperAttributesMember>();
AddName和AddMember中的每個類型都是基於ISourceToDestinationNameMapper和IChildMemberConfiguration接口的。也可以創建自己的類通過Lambda語句參數來配置屬性,因此你可以微調AutoMapper如何解析屬性映射。
自定義類型轉換
在作對象之間的轉換時,有些屬性的類型是不能直接轉換的,但經過驗證,默認可以將string類型映射為int和DateTime類型。
方法一:
ConvertUsing() :使用映射的方式
Ex: cfg.CreateMap<Source, Destination>().ConvertUsing(s =>
{
var d = new Destination();
d.Value1 = System.Convert.ToInt32(s.Value1);
d.Value2 = System.Convert.ToDateTime(s.Value2);
d.Value3 = Type.GetType(s.Value3);
return d;
});
方法二:
定一個類,該類型需要繼承 ITypeConverter泛型接口,源類型和目標類型,並且實現Convert方法
Ex: public class CustomTypeConverter : ITypeConverter<Source, Destination>
然后在Mapper.Initialize的時候使用ConvertUsing的泛型方法
Ex: cfg.CreateMap<Source, Destination>().ConvertUsing<CustomTypeConverter>();
方法三:
定一個類,該類型需要繼承ITypeConverter泛型接口,源基礎類型和目標基礎類型,並且實現Convert方法
Ex:public class TypeConverter : ITypeConverter<string, Type>
然后在Mapper.Initialize的時候創建該基類型的映射,該方式在Mapper全局都是有效的
Ex: Mapper.Initialize(cfg => {
cfg.CreateMap<string, Type>().ConvertUsing<TypeConverter>();
cfg.CreateMap<Source, Destination>();
});
自定義值解析
雖然AutoMapper覆蓋了很大一部分目標成員的映射場景,但還是有一部分需要自定義處理。因是對目標類的中的某一個屬性的賦值處理,因些會用到ForMember()方法。使用ResolveUsing() :指定賦值的方式。
方法一:
Ex:cfg.CreateMap<Source, Destination>().ForMember(dest => dest.Total, opt => {
opt.ResolveUsing(s =>
{
var destination = new Destination();
destination.Total = s.Value1 + s.Value2;
return destination.Total;
}); });
方法二:
創建一個類,並實現IValueResolver方法
Ex: public class MyValueResolver : IValueResolver<Source, Destination, int>
然后在創建映射關系的時候為其更改賦值方式
Ex: cfg.CreateMap<Source, Destination>().ForMember(dest => dest.Total, opt => { opt.ResolveUsing<MyValueResolver>(); });
Dynamic和ExpandoObject映射
Dynamic動態創建對象 Ex:dynamic dynamicObj = new ExpandoObject();//ExpandoObject對象包含可在運行時動態添加或移除的成員.
雖然Dynamic為動態對象不有辦法創建映射關系,但必須先對Mapper實例化,才能使用。
Ex: public static ExpandoObject DynamicAndExpandoObject()
{
Mapper.Initialize(cfg=> { });
dynamic dynamicObj = new ExpandoObject();
dynamicObj.Age = 12;
dynamicObj.Name = "Feng測試";
Person person = Mapper.Map<Person>(dynamicObj);
Console.WriteLine("person.Age={0},Name={1}", person.Age, person.Name);
dynamic dynamicSecond = Mapper.Map<ExpandoObject>(person);
dynamicSecond.Address = "北京";
Console.WriteLine("dynamicObj.Age={0},Name={1},Address={2}", dynamicSecond.Age, dynamicSecond.Name, dynamicSecond.Address);
return dynamicSecond;
}
扁平化
性相匹配。 如果對目標類型上的任何屬性,方法或以“Get”為前綴的方法不存在源類型上,則AutoMapper會將目標成員名稱拆分為單個單詞(遵循帕斯卡(PascalCase)拼寫法約定)。
Ex:public class Order
{
public Customer Customer { get; set; }
public decimal GetTotal()
{
return 100M;
}
}
public class Customer
{
public string Name { get; set; }
}
public class OrderDto
{
public string CustomerName { get; set; }
public decimal Total { get; set; }
}
當使用 Order與OrderDto映射時,Total屬性匹配到了Order上的GetTotal方法。CustomerName屬性匹配到了Order上的Customer.Name屬性。總之,只要合適地命名目標類型屬性,我們就不必配置單獨的屬性匹配。
List和數組
AutoMapper只要求元素類型的配置而不要求可能會用到的任何數組或者list類型。因此的創建映射的配置中,只是配置類型之間的映射,而不需要設計任何集合類型。
具體來說,支持的源集合類型包括:
-
- IEnumerable
- IEnumerable<T>
- ICollection
- ICollection<T>
- IList
- IList<T>
- List<T>
- Arrays
集合中的多態元素類型
AutoMapper支持多態數組和集合,因此如果發現派生的源或者目標類型就會自動轉換,但創建的時候需要聲明。
Ex:cfg.CreateMap<ParentSource, ParentDestination>().Include<ChildSource, ChildDestination>();
但也需要聲明子類型的映射,因為AutoMapper“猜不出”具體是哪個子類型
Ex:cfg.CreateMap<ChildSource, ChildDestination>();
也可以聲明子類型的映射,再包含基類型,但也要再聲明基類型的映射
Ex:cfg.CreateMap<ParentSource, ParentDestination>();
cfg.CreateMap<ChildSource, ChildDestination>().IncludeBase<ParentSource, ParentDestination>();
繼承映射屬性
對一個屬性的映射有多種方式,下面是這些源的優先級
-
- 顯式映射 (使用.MapFrom())
- 繼承的顯式映射
- 默認的映射 (通過默認匹配的屬性)
- 忽略的屬性映射
Ex: cfg.CreateMap<Order, OrderDto>().Include<PCOrder, OrderDto>().Include<MobileOrder, OrderDto>().ForMember(o => o.Referrer, m => m.Ignore());//這里配置了忽略目標屬性Referrer的映射
雖然在配置中忽略了Referrer的映射處理,但還是會將源類型中的Referrer屬性值給到目標。這就是優先級的問題。這就是因為 默認的映射比忽略的映射優先級高。
Null值替換
當源類型中某個屬性為空值的時候在映射到目標類型時,目標類型的該屬性可以設置一個默認值
NullSubstitute(),如果源屬性為空,則用該方法設置默認值。
Ex: cfg.CreateMap<Person, PersonInfo>().ForMember(dest => dest.Title, opt => opt.NullSubstitute("屌絲"));
開放泛型
所謂的開放泛型是指 源類型為一個泛型類,目標類型也是一個泛型類型。在創建兩個泛型類的映射時,中的泛型不需要填寫。
Ex: cfg.CreateMap(typeof(Source<>), typeof(Destination<>));
也可以使用一個泛型轉換器來進行處理。
Ex: cfg.CreateMap(typeof(Source<>),typeof(Destination<>))
.ConvertUsing(typeof(Converter<>));
投影
當把一個源值投影到一個不精准匹配源結構的目標值時,必須指明成員映射定義。
MapFrom() :指定源類型屬性的值。
Ex: .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))
學習來源:http://www.cnblogs.com/farb/p/4932692.html