AutoMapper中的Map和DynamicMap——高手注重細節,思考和總結

tkbSimplest發表於2015-11-27

近日在做專案的時候,遇到了個怪問題,關於AutoMapper的細節問題,也是不為一般人所關注的。

本人研究AutoMapper也沒有多長時間,而且研究的過程中也寫了關於AutoMapper的系列基礎教程,但是畢竟AutoMapper是個開源專案,並不是一個簡單的系列教程就能解釋的清楚的,只能解釋個大概,專案實戰的時候,遇到的細節問題還得自己私下裡再次研究、總結。

首先,我要說明的是,這篇部落格的寫作順序是按事情發展的順序來寫的,也就是說,在我想寫這篇部落格時,問題的根本原因還沒找到,但是此時,我回過頭來,再來看這個過程中每個問題的解決思路,我想,此時這個細節的問題已經清楚了,也來分享一下。

 

先來讓大家看看我的AutoMapper的大致配置過程:

  • 先建立實體類對應的AutoMapper配置類,命名規範是EntityName(實體類名)+Profile,比如PersonProfile,ProvincePerfile等。
public class ProvinceProfile : Profile
{
    protected override void Configure()
    {
        Mapper.Initialize(cfg => cfg.CreateMap<Provinces, ProvinceDto>()
            .ForSourceMember(src=>src.UpdatedDate,opt=>opt.Ignore())
            .ReverseMap());
    }
}
  • 再建立一個靜態類,取名AutoMapperConfig,然後在他的靜態方法中初始化Mapper,並新增所有的配置類,這裡我還新增了其他配置類。
public static class AutoMapperWebConfig
{
    public static void Configure()
    {
        Mapper.Reset();
        Mapper.Initialize(cfg =>
        {
            cfg.AddProfile<ProvinceProfile>();
            cfg.AddProfile<CityProfile>();
            cfg.AddProfile<StationProfile>();
            cfg.AddProfile<TerminalDeviceProfile>();//TerminalDeviceProfile依賴ProvinceProfile
            cfg.AddProfile<OperatorProfile>();  
        });
        Mapper.AssertConfigurationIsValid();//驗證所有的對映配置是否都正常
    }
}
  • 最後,在專案啟動的時候(ASP.Net程式在.asax檔案中的Application_Start方法)呼叫AutoMapperWebConfig.Configure();就可以了。

這一切都感覺這麼順利,但是往往越是順利的時候,也意味著不順快來了。接下來,類似下面截圖中的錯誤向我狂轟亂炸。

當我使用在應用層使用Mapper.Map()方法將實體類對映為Dto類時,報錯如下:

E{A2JHI%`4A68KE]KR6I_}3

很明顯這是AutoMapper對映錯誤。

接下來就各種搜尋錯誤,找到了下面一篇部落格,原文

ta的解決方案是,如圖

image

看到這裡,我很高興,趕緊改了一下自己的程式碼,發現果然成功了!但是該博主並沒有給出個所以然來。

L]%SFP7IHKNLMU9V(_K}}{S

但是,我不服氣,我之前已經建立了兩個類之間的對映啊,為啥Mapper.Map()方法不行,我就納悶了,我非得搞清楚這兩者之間的關係不可。

我尋思著,就字面意思來看,一個是“對映”,一個是“動態對映”,會有啥區別呢?

於是各種搜兩者之間的區別,去StackOverflow上找到了下面的答案:原連結

image

這裡說,DynamicMap在編譯時你不知道源型別的情況下使用,那麼,相應地,Map就是在編譯時知道源型別的情況下使用。簡單的這一句解釋並不能讓接觸AutoMapper時間不長的人有所啟示。

而問題就出在,我之前已經建立了對映,所以在編譯時應該可以確定源型別的,更何況我這兩個類都很簡單,不可能是因為資料型別不一致造成的對映失敗啊!程式碼如下:

public class Provinces : Entity
{
    public virtual string Code{ get; set; }

    public virtual string Name{ get; set; }
    public virtual string UpdatedBy{ get; set; }
    public virtual DateTime? UpdatedDate{ get; set; }
    public Provinces(){}
}
public class ProvinceDto:EntityDto
{
    public string Code { get; set; }
    public string Name { get; set; }
}

 

於是,還得不到自己想要的答案,就去GitHub上AutoMapper的專案下Open了一個Issue,點選檢視我提的Issue,在這裡,我得到了我想要的答案。

image

該回答者給我的答案如上,他猜想我在每個實體類對應的Profile檔案中,應該直接使用CreateMap,而不是在Mapper.Initialize中使用CreateMap。按照他的提示,我修改了程式碼,以後再也沒有出現錯誤。

=============該總結了================

現在再次回頭看看這個問題,完全是瞭然於胸的感覺。因為我在AutoMapperWebconfig靜態類的靜態方法中已經進行了Mapper.Initialize(),這是AutoMapper的初始化,而在每個實體類對應的Profile類中又使用了一次。嘗試著去想一想,第一次將所有的文配置類都初始化到Mapper(暫且先將它理解成一個容器)中,第二次呼叫Mapper.Initialize()可能會把之前的內容都擦除掉,所以使用Mapper.Map的時候會報錯就可以想明白了,而使用DynamicMap,之前在哪裡看到過DynamicMap就想當於先CreateMap,再Map,所以,我們之前的配置就可有可無,無關緊要了,因為DynamicMap會把之前的配置擦除掉,所以上面截圖中的對映成功了。而至於其他配置類沒有出現該錯誤,該回答者給的答案是”這可能是個意外!“。在這次坎坷曲折的解決問題的過程中,還是學到了很多東西的。在這裡,我們也很顯然,可以看出部落格園那位園友使用DynamicMap成功後而沒給出為什麼,十有八九也是我這個問題了。

==============總結完畢================

相關文章