前言
談到對象映射器,AutoMapper 知名度是非常的高,但很少有人知道 Mapster。而為什麼選擇 Mapster 呢?
理由一:性能優于 AutoMapper ,相關測試位于https://github.com/MapsterMapper/Mapster上檢視。
理由二:多學習一項技能
網上查了一下,關于 Mapster 的資料非常少,是以在這裡我們詳細寫下它的用法,以幫助更多的序員寶快速掌握它的用法。
官方文檔:https://github.com/MapsterMapper/Mapster/wiki
安裝 Mapster
PM> Install-Package Mapster
或者
dotnet add package Mapster
定義實體
目的:使用 Mapster 實作 User 到 UserDto 的映射
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public string like { get; set; }
}
public class UserDto
{
public string name { get; set; }
public int UserAge { get; set; }
public string UserSex { get; set; }
public string like { get; set; }
}
簡單使用
/*
* 預設情況下,無需任何配置,Mapster會根據兩個實體字段名稱相同進行比對
* 第一次調用時,配置會被緩存,第二次将會從緩存中取,以此提升性能
*/
var user = new User();
var dto = user.Adapt<UserDto>();//映射為新對象
user.Adapt(dto);//在目标對象的基礎上進行映射
//注意:Adapt擴充方法使用的配置為 `TypeAdapterConfig.GlobalSettings`
Mapster 配置 (TypeAdapterConfig)
可以直接使用 Mapster 内置的全局靜态配置
TypeAdapterConfig.GlobalSettings
,也可以執行個體化一個配置
new TypeAdapterConfig()
推薦使用執行個體化的方式,對 TypeAdapterConfig 進行映射配置。
注:Mapster 預設比對規則是相同字段名之間進行映射。
方式一
直接在 TypeAdapterConfig 配置對象的映射關系
var config = new TypeAdapterConfig();
//映射規則
config.ForType<User, UserDto>()
.Map(dest => dest.UserAge, src => src.Age)
.Map(dest => dest.UserSex, src => src.Sex);
var mapper = new Mapper(config);//務必将mapper設為單執行個體
var user = new User{Name = "xiaowang",Age = 18,Sex = "boy"};
var dto = mapper.Map<UserDto>(user);
字段帶有前字尾,可以使用
NameMatchingStrategy.ConvertDestinationMemberName
對目标字段名稱進行替換,使得它和源字段名稱相同。
還有替換源字段的方法
NameMatchingStrategy.ConvertSourceMemberName
注意:如果一個定義多個
ForType
,後定義的會覆寫先定義的規則,是以隻有最後定義的規則會生效
NameMatchingStrategy
var config = new TypeAdapterConfig();
//使用
config.ForType<User, UserDto>()
.NameMatchingStrategy(NameMatchingStrategy.ConvertDestinationMemberName(dest => dest.Replace("User", "")));
方式二
使用接口的方式,需要實作
IRegister
//實作接口 IRegister
public class UserDtoRegister : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.ForType<User,UserDto>()
Map(dest => dest.UserAge, src => src.Age);
//...
}
}
//執行個體化Mapper
var config = new TypeAdapterConfig();
//var config = TypeAdapterConfig.GlobalSettings;
//隻有要給定 IRegister 所在的程式集名稱,Mapster 會自動識别 IRegister,進行配置注入。
config.Scan("程式集名稱1","程式集名稱2");
var mapper = new Mapper(config);//務必設定為單執行個體
忽略字段
var config = new TypeAdapterConfig();
//映射規則
config.ForType<User, UserDto>()
.Map(dest => dest.UserAge, src => src.Age)
.Map(dest => dest.UserSex, src => src.Sex);
.IgnoreNullValues(true)//忽略空值映射
.Ignore(dest => dest.UserAge)//忽略指定字段
.IgnoreAttribute(typeof(DataMemberAttribute))//忽略指定特性的字段
.NameMatchingStrategy(NameMatchingStrategy.IgnoreCase)//忽略字段名稱的大小寫
.IgnoreNonMapped(true);//忽略除以上配置的所有字段
config.ForType<User,UserDto>()
.IgnoreMember((member, side) => !member.Type.Namespace.StartsWith("System"));//實作更細緻的忽略規則
member
和
side
分别對應
IMemberModel
MemberSide
,這裡我貼出相應的源碼。
//包含了映射類型的資訊
public interface IMemberModel
{
Type Type { get; }
string Name { get; }
object? Info { get; }
AccessModifier SetterModifier { get; }
AccessModifier AccessModifier { get; }
IEnumerable<object> GetCustomAttributes(bool inherit);
}
//辨別目前是源類型還是目标類型
public enum MemberSide
{
Source = 0,
Destination = 1
}
分支(Fork)
Mapster 的 Fork 功能允許我們定義局部的映射規則,并且分支不會重複編譯,不需要考慮性能問題。
var config = new TypeAdapterConfig();
var mapper = new Mapper(config);
var user = new User{Name = "xiaowang",Age = 18,Sex = "boy"};
var dto = mapper.From(user).ForkConfig(forked =>
{
//該分支規則,不會重複編譯,僅限該語句中有效,不會影響config的配置
forked.ForType<User, UserDto>().Map(dest => dest.name, src => src.Name);
})
.AdaptToType<UserDto>();//映射為新對象
dto = mapper.From(user).ForkConfig(forked =>
{
forked.ForType<User, UserDto>().Map(dest => dest.name, src => src.Name);
})
.AdaptTo(new UserDto());//在目标對象基礎上進行映射
NewConfig 方法
NewConfig 方法允許我們對兩個類型之間建立配置,如果兩個類型之前配置了映射關系,則 NewConfig 方法會覆寫之前的配置
var config = new TypeAdapterConfig();
config.ForType<User,UserDto>().Map(dest => dest.UserAge, src => src.Age);
//...
//覆寫 User 和 UserDto 之前的配置
config.NewConfig<User,UserDto>().Map(dest=>dest.UserAge,src=>100);
//擴充知識:覆寫Mapster預設靜态配置
TypeAdapterConfig<User,UserDto>.NewConfig().Default.NameMatchingStrategy(NameMatchingStrategy.IgnoreCase);
運作時傳參
允許運作時傳入資料,幹預映射過程
var config = new TypeAdapterConfig();
config.ForType<User, UserDto>()
.Map(dest => dest.name, src => MapContext.Current.Parameters["userName"]);//配置運作時參數
var mapper = new Mapper(config);
//使用時傳入資料
var user = new User();
var dto = mapper.From(user).BuildAdapter().AddParameters("userName","xiaowang").AdaptToType<UserDto>();
其他知識點
我們盡量不要把實體間的映射規則配置到
TypeAdapterConfig.GlobalSettings
(預設配置)。随着業務的發展,一個配置很難兼顧所有業務,可能會出行沖突的情況,相對複雜的業務,可以建立一個
TypeAdapterConfig
,或者使用
config.Clone()
能輕松複制一份配置。全局配置可以放一些簡單的配置項,例如:映射時忽略大小寫。
注意:Adapt 擴充方法使用的是 TypeAdapterConfig.GlobalSettings
小技巧
Adapt 擴充方法的使用
Dictionary<string,object> dict = new User().Adapt<Dictionary<string,object>>();//object 到 Dictionary 的轉換
string s = 123.Adapt<string>(); //equal to 123.ToString();
int i = "123".Adapt<int>(); //equal to int.Parse("123");
攔截映射前後
BeforeMapping
方法和
AfterMapping
//BeforeMapping 映射執行前
config.ForType<User, UserDto>().BeforeMapping((src, dest) =>
{
src.Age = 100;
dest.UserAge = src.Age + 10;
});
//AfterMapping 映射執行後
//...
更多的使用技巧可以檢視官方文檔:https://github.com/MapsterMapper/Mapster/wiki