【5min+】 物件對映只有AutoMapper?試試Mapster

句幽發表於2020-03-19

系列介紹

【五分鐘的dotnet】是一個利用您的碎片化時間來學習和豐富.net知識的博文系列。它所包含了.net體系中可能會涉及到的方方面面,比如C#的小細節,AspnetCore,微服務中的.net知識等等。 5min+不是超過5分鐘的意思,"+"是知識的增加。so,它是讓您花費5分鐘以下的時間來提升您的知識儲備量。

正文

一談到如何在.Net中進行物件對映,可能大部分同學都會脫口而出:“使用AutoMapper!”。 是的,AutoMapper 是一個非常成熟的物件對映器。截至到寫這篇文章,您能在Nuget上下載到的AutoMapper包的版本為:v9.0.0,而對應的 Github 的 star 已經高達7K。

對了,談到AutoMapper就不得不談起它的作者(之一):“JIMMY BOGARD”。也許您沒有聽過這個名字,但是您一定聽過他的另一個作品:MediatR(在微軟的官方示例EShop中也使用了MediatR)。同時,“JIMMY BOGARD” 也是提出“將領域事件附加在聚合根”上的人,為領域驅動設計(DDD)做出了很大的貢獻。在微軟官方文件中,您可以看到該處提及到了“JIMMY BOGARD”:

x

好吧,優秀的人總是優秀?。還是回到今天的正文,物件對映工具。當然,對於AutoMapper大家可能再熟悉不過了,而且它的知名度和熱度也居高不下,看一看百度搜尋結果就知道了:

x

然後再來看一看,我們們今天要介紹的主角:Mapster。 不知道有多少同學聽過它?應該很少吧,這一點從百度搜尋也可以看出來:

x

額………………好像差距有點大哈。而且在這些搜尋結果中,有用的資訊只有那麼幾條,其中能看的文章就只有一條,而且還是出自於部落格園。 來自 “dudu” 大佬去年的一篇文章: EF Core 相關的千倍效能之差: AutoMapper ProjectTo VS Mapster ProjectToType。 再來看一看Github的情況,距離我寫這一篇稿子的時候,Star數只有 518 個。

x

一個契機

我們們先來回顧一下AutoMapper是怎麼使用的:

現在有兩個類,一個叫做MyEntity ,一個叫做 MyDto。 在我們們書寫應用層程式碼的時候,將資料轉換為Dto是很常見的一種操作,所以這也是我們需要物件對映器的原因。 假設,這兩個類的結構是醬紫的:

public class MyEntity
{
    public string Name { get; set; }

    public int No { get; set; }
}

public class MyDto
{
    public string Name { get; set; }

    public int No { get; set; }
}
複製程式碼

很普遍,也是很常見的型別。那麼如果我們要用AutoMapper來完成兩者之間的轉換呢?

var configuration = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<MyEntity, MyDto>();
});

var mapper = configuration.CreateMapper();

var r = mapper.Map<MyDto>(new MyEntity() { Name = "xxx", No = 111 });
複製程式碼

這是9.0的版本,如果您用過以前的版本可能會有點差異,比如老版本會使用Initialize方法來配置。但是思路都是一樣的,也就是說,我們們需要先配置物件與物件之間的相互關係,然後建立一個Mapper,在.NET core中我們們一般會在Configura配置好之後,將mapper註冊為一個單例,以後使用的話通過依賴注入就可以使用了。

是的,這種寫法邏輯清晰沒有一點問題。那麼是什麼契機讓我選擇放棄AutoMapper呢? 可能您會認為是效能問題,畢竟在上面 dudu 的那篇文章的標題真的很有吸引力。 但這只是很小的一部分原因。

當我在寫一些庫的時候,我需要用到物件轉換的功能,如果自己造輪子寫一個的話也不現實(可以看看AutoMapper的原始碼,裡面有多少的表示式樹寫法?),所以我嘗試引入第三方的對映工具,和大家一樣我第一反應就是AutoMapper。但是在評估的時候,我發現:一般來說,mapper物件全域性只需要一個,那麼這個mapper物件是在我寫的庫中使用,還是交由使用者來建立呢? 如果在庫中建立,那麼使用者必須在使用庫的時候進行配置,比如庫公開一個委託來配置:

service.AddMyLibary(config=>
{
    //config wrap automapper
})
複製程式碼

就好比上面的寫法。可能您現在正在使用的框架中就是使用了這種方式。 當然也不是說這樣不好,但是我個人感覺很奇怪。

還有一點就是,AutoMapper必須要在進行了配置之後才能完成對映,如果我不提供配置的話,就是丟擲一個異常。

所以,基於這兩點,我就想有沒有 1:簡單的對映不需要配置 2:可以在任何地方進行配置 的物件對映工具。

是的,後來我採用了Mapster,很早之前就已聽聞該工具,但是一直沒有對比著使用過它。

如果將上面AutoMapper進行對映的程式碼修改一下,轉換為Mapster的版本,是這樣的:

var entity = new MyEntity() { Name = "xxx", No = 111 };
var r = entity.Adapt<MyDto>();
複製程式碼

是的,沒有看錯,只有一句程式碼。(雖然我寫了兩句?)。 當我們安裝了Mapster之後,object物件就會擁有一個 Adapt() 的擴充套件方法。只需要呼叫該方法就可以直接完成轉換。對於簡單的關係,我們根本都不需要進行配置。

那麼對於複雜的對映呢? Mapster 提供了一個 TypeAdapterConfig<T,T> 的靜態泛型型別來進行配置,所以我們可以在任何地方書寫配置:

TypeAdapterConfig<MyEntity, MyDto>
    .NewConfig()
    .Map(s => s.Name, d => d.Name + "_Mapster");
複製程式碼

就像這樣,我們就完成了這組物件的複雜對映關係。(Name裡面加上"_Mapster"字尾)。

小試牛刀

當然,上面的例子只是一個很基礎的型別,但是我們經常會遇到型別裡面擁有另外的型別,這種巢狀關係能行嗎? 所以我們把上面的實體進行更改:

public class MyDto
{
    public string Name { get; set; }

    public int No { get; set; }

    public string UoyoName { get; set; }
}

public class MyEntity
{
    public string Name { get; set; }

    public int No { get; set; }

    public ChildEntity UoyoCSharp { get; set; }
}

public class ChildEntity
{
    public string Name { get; set; }
}
複製程式碼

在MyEntity裡面擁有一個ChildEntity的型別。 “什麼?您問我為什麼不好好命名,比如ChildEntity就命名為Child呀,為什麼要命名成讀不懂的東西。” 因為……您命名規範了,根本都不用寫配置,Mapster會自動完成對映。 所以基於這個不規則的命名,我們只需要進行下面的配置就行了:

TypeAdapterConfig<MyEntity, MyDto>
           .NewConfig()
           .Map(s => s.UoyoName, d => d.UoyoCSharp.Name);
複製程式碼

就是這麼簡單。那麼其它的高階對映呢??? 請自行跳轉自文件頁查詢。 因為本文不是教程篇所以就偷懶了哈。當然官方的文件也很少,只需要半個小時,可能您就學完了?。

最後,再來說一說大家很關心的一個問題吧:它和AutoMapper比較,效能有什麼差距呢? 由於選型評估的時候我也並沒有太考慮效能這個因素,所以就沒有進行測試,但是在Github的說明頁,官方給了一個測試比較:

x

好吧,差距相對來說還是挺大的。但是畢竟我沒有進行確切的驗證,也不會對它進行無腦吹。詳細情況還請各位大佬自行測試。

最後,小聲說一句:點個推薦吧.....

x

相關文章