AutoMapper(一)

tkbSimplest發表於2015-11-03

返回總目錄


對映前後操作

偶爾有時候,在對映發生之前或之後,你可能需要執行一些自定義的邏輯。這可能是很少見的事情,因為在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)範圍內的外星人對映成人,執行結果如下,和預測結果一致。

image

配置

初始化

初始化是配置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; }

結果如下,和預測結果一致:

image

可見,這種對映配置就是告訴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; }

結果如下,和預測結果一致:

image

識別字首/字尾

有時候你的源型別和目標型別的屬性有公共的字首/字尾,由於這些命名不匹配的緣故,使你不得不處理這些自定義的成員對映。為了實現這個,可以識別前字尾:

小試牛刀:

在Aliens類中新增如下程式碼:

public string PGender { get; set; }

在Person類中新增如下程式碼:

public string Gender { get; set; }

可見,源型別中有一個字首為“P”的欄位PGender,要想成功對映為Person,就必須識別出字首“P”,所以在Mapper的配置中新增程式碼(注意,新增到CreateMap方法之前,否則無效):

cfg.RecognizePrefixes("P");

結果如下,和預測結果一致:

image

預設情況下,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

結果和預測一樣:

image

解釋一下:

我們在配置中新增了過濾器,只允許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>();
});

相關文章