返回總目錄
映射前后操作
偶爾有時候,在映射發生之前或之后,你可能需要執行一些自定義的邏輯。這可能是很少見的事情,因為在AutoMapper之外處理這些事情是更明顯的。你可以創建一個映射前后的全局操作:
Mapper.CreateMap<Source, Dest>() .BeforeMap((src, dest) => src.Value = src.Value + 10) .AfterMap((src, dest) => dest.Name = "John");
或者在映射期間,創建映射前后的回調函數:
int i = 10; Mapper.Map<Source, Dest>(src, opt => { opt.BeforeMap((src, dest) => src.Value = src.Value + i); opt.AfterMap((src, dest) => dest.Name = HttpContext.Current.Identity.Name); });
當你需要將上下文信息注入映射前后的行為中時,后者的配置很有用。
條件映射
在屬性映射之前,AutoMapper允許將必須滿足的條件添加到屬性上。
這個用在下面這種情況,比如有兩個類,分別是Aliens(外星人)和Person(人)類,都有一個Age屬性,我們都知道我們人類的年齡都是非負數,所以我們這里用unsigned int(無符號整數)類型。但是,目前人類科技水平有限,還沒有研究出是否有外星人存在,就更無法確定其年齡屬性了,所以我們這里假設外星人的年齡可以為負數(如果你反駁我你也沒依據啊,暫且就這樣吧),那么就定義其為int類型,如果我們要將外星人映射到人類上,其實就是uint到int之間的映射:
namespace FrontAutoMapper { class Program { static void Main(string[] args) { //創建映射,映射條件是源類型的Age屬性在區間(0,149)范圍內 Mapper.CreateMap<Aliens, Person>().ForMember(dest => dest.Age, opt => opt.Condition(src => src.Age > 0 && src.Age < 149)); var p1 = Mapper.Map<Person>(new Aliens() { Age = -1 });//不符合映射條件 var p2 = Mapper.Map<Person>(new Aliens() { Age = 0 });//不符合映射條件 var p3 = Mapper.Map<Person>(new Aliens() { Age = 1 });//符合映射條件 var p4 = Mapper.Map<Person>(new Aliens() { Age = 148 });//符合映射條件 var p5 = Mapper.Map<Person>(new Aliens() { Age = 149 });//不符合映射條件 Console.WriteLine(p1.Age);//映射不成功,返回Person.Age默認值22 Console.WriteLine(p2.Age);//映射不成功,返回Person.Age默認值22 Console.WriteLine(p3.Age);//映射成功,返回新值1 Console.WriteLine(p4.Age);//映射成功,返回新值148 Console.WriteLine(p5.Age);//映射不成功,返回新值22 Console.Read(); } } public class Person { public Person() { Age = 22; } public uint Age { set; get; }//Person的Age屬性默認值是22 } public class Aliens { public Aliens() { Age = -23; } public int Age { get; set; }//Aliens的Age屬性默認值是-23 } }
這個例子是將年齡Age在區間(0,149)范圍內的外星人映射成人,執行結果如下,和預測結果一致。
配置
初始化
初始化是配置AutoMapper受人歡迎的模式,每個應用域應該配置一次:
//初始化配置文件 Mapper.Initialize(cfg => { cfg.CreateMap<Aliens, Person>(); cfg.AddProfile<AliensPersonProfile>();//添加一個配置文件 });
映射配置是靜態的,此后不應該改變了。
Profile實例
可用來組織AutoMapper配置
namespace ConditionalMapping { public class AliensPersonProfile : Profile { protected override void Configure() { //放一些CreateMap(...)等映射配置操作 } } }
自定義一個繼承了Profile類的類,然后重寫Configure方法,在該方法中放一些映射的配置。
像下面這樣初始化自定義的配置文件:
//初始化配置文件 Mapper.Initialize(cfg => { cfg.AddProfile<AliensPersonProfile>();//添加一個配置文件 });
命名慣例
可以設置源和目標的命名慣例。
//初始化配置文件 Mapper.Initialize(cfg => { cfg.SourceMemberNamingConvention=new LowerUnderscoreNamingConvention(); cfg.DestinationMemberNamingConvention=new PascalCaseNamingConvention(); });
也可以在配置文件中的Configure方法中設置:
protected override void Configure() { SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); DestinationMemberNamingConvention = new PascalCaseNamingConvention(); }
小試牛刀:
在Aliens類中添加如下代碼:
public string MyName { set; get; }
在Person類中添加如下代碼:
public string my_name { get; set; }
結果如下,和預測結果一致:
可見,這種映射配置就是告訴AutoMapper這兩種命名慣例可以相互映射。而且需要注意的是,這兩句代碼可放在CreateMap()方法之后。
替換字符
在成員名字映射期間,也可以替換個別的字符或者整個單詞。
配置Mapper(可在Initialize方法或配置文件中均可,后面不再提示),注意一定要在CreateMap方法之前,配置替換字符,否則沒有效果。這也符合邏輯,只有先添加替換條件,才能按條件映射嘛。
cfg.ReplaceMemberName("Ä", "A"); cfg.ReplaceMemberName("í", "i"); cfg.ReplaceMemberName("Tool", "Car"); cfg.AddProfile<AliensPersonProfile>();
小試牛刀:
在Aliens類中添加如下代碼:
public int Ävíator { get; set; } public string ToolName { get; set; }
在Person類中添加如下代碼:
public int Aviator { get; set; } public string CarName { get; set; }
結果如下,和預測結果一致:
識別前綴/后綴
有時候你的源類型和目標類型的屬性有公共的前綴/后綴,由於這些命名不匹配的緣故,使你不得不處理這些自定義的成員映射。為了實現這個,可以識別前后綴:
小試牛刀:
在Aliens類中添加如下代碼:
public string PGender { get; set; }
在Person類中添加如下代碼:
public string Gender { get; set; }
可見,源類型中有一個前綴為“P”的字段PGender,要想成功映射為Person,就必須識別出前綴“P”,所以在Mapper的配置中添加代碼(注意,添加到CreateMap方法之前,否則無效):
cfg.RecognizePrefixes("P");
結果如下,和預測結果一致:
默認情況下,AutoMapper識別前綴“Get”,如果你需要清除前綴:
cfg.ClearPrefixes();
全局屬性/字段過濾
默認情況下,AutoMapper映射每一個公共的屬性/字段。你可以使用屬性/字段過濾器過濾出不需要映射的屬性/字段:
//不映射任何字段 cfg.ShouldMapField = fi => false; //只映射getter是private的屬性 cfg.ShouldMapProperty = pi => pi.GetMethod != null && pi.GetMethod.IsPrivate; cfg.AddProfile<AliensPersonProfile>();
小試牛刀:
分別給Aliens和Person類添加下面這段代碼:
private string code; public string Code { private get { return code; } set { code = value; } }
Main方法中添加代碼:
var p10 = Mapper.Map<Person>(new Aliens() { Age = 44,Code = "111"}); Console.WriteLine(string.Format("Person.Age={0},Person.code={1}",p10.Age,p10.code));//22,111
結果和預測一樣:
解釋一下:
我們在配置中添加了過濾器,只允許getter是private的屬性映射,而Person的Age屬性getter默認是public的,所以沒有將我們給的值44映射到,因而返回默認的22,而Person的Code屬性的getter是私有的,所以映射到了,但是要想取到映射后的值,我們只能通過公有的字段來獲得。
配置可見性
默認情況下,AutoMapper只識別公有成員。雖然可以映射到私有的setter,但是如果整個屬性是private/internal的,那么就會跳過internal/private的方法和屬性。為了命令AutoMapper識別具有其他可見性的成員,重寫默認的過濾器ShouldMapField或ShouldMapProperty:
Mapper.Initialize(cfg => { // 映射具有public或internal的getter的屬性 cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly; cfg.CreateMap<Source, Destination>(); });