C# Mapster 對象映射器(C#對象映射器)


前言

談到對象映射器,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"));//實現更細致的忽略規則

memberside分別對應IMemberModelMemberSide,這里我貼出相應的源碼。

//包含了映射類型的信息
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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM