Mapster 高效能物件對映框架

yswenli發表於2024-04-09

Mapster 簡介

Mapster 是一個使用簡單,功能強大,效能極佳的物件對映框架

為什麼選擇 Mapster ?

效能 & 記憶體佔用

與 AutoMapper 相比,Mapster 在速度和記憶體佔用方面表現更加優秀,可以在只使用1/3記憶體的情況下獲得4倍的效能提升。
並且透過使用以下元件可以獲得更高的效能:

Method Mean StdDev Error Gen 0 Gen 1 Gen 2 Allocated
'Mapster 6.0.0' 108.59 ms 1.198 ms 1.811 ms 31000.0000 - - 124.36 MB
'Mapster 6.0.0 (Roslyn)' 38.45 ms 0.494 ms 0.830 ms 31142.8571 - - 124.36 MB
'Mapster 6.0.0 (FEC)' 37.03 ms 0.281 ms 0.472 ms 29642.8571 - - 118.26 MB
'Mapster 6.0.0 (Codegen)' 34.16 ms 0.209 ms 0.316 ms 31133.3333 - - 124.36 MB
'ExpressMapper 1.9.1' 205.78 ms 5.357 ms 8.098 ms 59000.0000 - - 236.51 MB
'AutoMapper 10.0.0' 420.97 ms 23.266 ms 35.174 ms 87000.0000 - - 350.95 MB

基本用法

對映到一個新的物件

Mapster 建立 目標物件 並將符合規則的成員對映到目標物件中:

var destObject = sourceObject.Adapt<Destination>();

對映到現有物件

建立一個物件,Mapster將把 源物件 對映到這個物件:

sourceObject.Adapt(destObject);

Queryable Extensions

Mapster 還提供了對 IQueryable 的對映擴充套件:

using (MyDbContext context = new MyDbContext())
{
    // 使用 ProjectToType 對映到目標型別
    var destinations = context.Sources.ProjectToType<Destination>().ToList();

    // 手動編寫對映
    var destinations = context.Sources.Select(c => new Destination {
        Id = p.Id,
        Name = p.Name,
        Surname = p.Surname,
        ....
    })
    .ToList();

對映器

擴充套件方法

可以從任何地方呼叫 Adapt 方法。

var dest = src.Adapt<TSource, TDestination>();

或者直接

var dest = src.Adapt<TDestination>();

這兩個擴充套件方法做的都是同樣的事情。src.Adapt<TDestination> 將把 src 轉換為 object 型別。因此,如果要轉換的是值型別,那麼請使用 src.Adapt<TSource, TDestination> 以避免不必要的裝箱/拆箱。

對映器例項 ( Mapper )

在一些情況下,需要將 對映器 或 工廠函式 傳遞到依賴注入容器中。Mapster 提供了 IMapperMapper 來滿足這個需求:

IMapper mapper = new Mapper();

並且使用 Map 函式來執行對映:

var result = mapper.Map<TDestination>(source);

構建器 ( Builder )

在大多數情況下,Adapt方法就足夠了,但有時需要使用構建器來支援一些特殊的場景。

一個基本的例子 —— 傳遞執行時的值:

var dto = poco.BuildAdapter()
              .AddParameters("user", this.User.Identity.Name)
              .AdaptToType<SimpleDto>();

如果使用 IMapper 例項,你可以透過 From 建立構建器。

var dto = mapper.From(poco)
              .AddParameters("user", this.User.Identity.Name)
              .AdaptToType<SimpleDto>();

資料型別

基本型別

基本型別的轉換 ,例如: int/bool/dobule/decimal ,包括可空的基本型別。

只要C#支援型別轉換的型別,那麼在 Mapster 中也同樣支援轉換。

decimal i = 123.Adapt<decimal>(); //equal to (decimal)123;

列舉型別

Mapster 會自動把列舉對映到數字型別,同樣也支援 字串到列舉 和 列舉到字串的對映。

.NET 預設實現 列舉/字串 轉換非常慢,Mapster 比 .NET 的預設實現快兩倍。

在 Mapster 中,字串轉列舉,如果字串為空或空字串,那麼列舉將初始化為第一個列舉值。

在Mapster中,也支援標記的列舉。

var e = "Read, Write, Delete".Adapt<FileShare>();  
//FileShare.Read | FileShare.Write | FileShare.Delete

對於不同型別的列舉,Mapster 預設將值對映為列舉。呼叫 EnumMappingStrategy 方法可以指定列舉對映方式,如:

TypeAdapterConfig.GlobalSettings.Default
    .EnumMappingStrategy(EnumMappingStrategy.ByName);

字串型別

在 Mapster 中,將其它型別對映為字串時,Mapster 將呼叫型別的 ToString 方法。

如果將字串對映為型別時,Mapster 將呼叫型別的 Parse 方法。

var s = 123.Adapt<string>(); // 等同於: 123.ToString();
var i = "123".Adapt<int>();  // 等同於: int.Parse("123");

集合

包括列表、陣列、集合、包括各種介面的字典之間的對映: IList<T>, ICollection<T>, IEnumerable<T>, ISet<T>, IDictionary<TKey, TValue> 等等…

var list = db.Pocos.ToList();
var target = list.Adapt<IEnumerable<Dto>>();  

可對映物件

Mapster 可以使用以下規則對映兩個不同的物件

  • 源型別和目標型別屬性名稱相同。 例如: dest.Name = src.Name
  • 源型別有 GetXXXX 方法。例如: dest.Name = src.GetName()
  • 源型別屬性有子屬性,可以將子屬性的賦值給符合條件的目標型別屬性,例如: dest.ContactName = src.Contact.Namedest.Contact_Name = src.Contact.Name

示例:

class Staff {
    public string Name { get; set; }
    public int GetAge() { 
        return (DateTime.Now - this.BirthDate).TotalDays / 365.25; 
    }
    public Staff Supervisor { get; set; }
    ...
}

struct StaffDto {
    public string Name { get; set; }
    public int Age { get; set; }
    public string SupervisorName { get; set; }
}

var dto = staff.Adapt<StaffDto>();  
//dto.Name = staff.Name, dto.Age = staff.GetAge(), dto.SupervisorName = staff.Supervisor.Name

可對映物件型別包括:

  • 結構體
  • 介面
  • 實現 IDictionary<string, T> 介面的字典型別
  • Record 型別 (類、結構體、介面)

物件轉換為字典的例子:

var point = new { X = 2, Y = 3 };
var dict = point.Adapt<Dictionary<string, int>>();
dict["Y"].ShouldBe(3);

Record 型別的例子:

class Person {
    public string Name { get; }
    public int Age { get; }

    public Person(string name, int age) {
        this.Name = name;
        this.Age = age;
    }
}

var src = new { Name = "Mapster", Age = 3 };
var target = src.Adapt<Person>();

自動對映 Record 型別有一些限制:

  • Record 型別屬性必須沒有 set
  • 只有一個非空建構函式
  • 建構函式中的所有引數名稱必須與屬性名稱相同

如果不符合以上規則,需要增加額外的 MapToConstructor 配置

對映配置

對映配置

使用 TypeAdapterConfig<TSource, TDestination>.NewConfig()TypeAdapterConfig<TSource, TDestination>.ForType() 配置型別對映;

當呼叫 NewConfig 方法時,將會覆蓋已存在的型別對映配置。

TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .Ignore(dest => dest.Age)
    .Map(dest => dest.FullName,
        src => string.Format("{0} {1}", src.FirstName, src.LastName));

如若不想覆蓋之前已經建立好的對映配置,可以使用 TypeAdapterConfig<TSource, TDestination>.ForType()

ForType 方法與 NewConfig 的差別:如果指定型別對映配置不存在,那它將建立一個新的對映,如果指定型別的對映配置已存在,那麼它將會擴充套件已有的對映配置,而不是刪除或替換已有的對映配置。

TypeAdapterConfig<TSource, TDestination>
        .ForType()
        .Ignore(dest => dest.Age)
        .Map(dest => dest.FullName,
            src => string.Format("{0} {1}", src.FirstName, src.LastName));

全域性設定

使用全域性設定將對映策略應用到所有的對映配置。

TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true);

對於特定的型別對映,你可以使用 TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig() 覆蓋全域性對映配置。

TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig().PreserveReference(false);

基於規則的對映配置

你可以使用 When 方法,當滿足某個條件時,進行一些特定的對映操作。

下面的這個例子,當任何一個對映的 源型別和目標型別 相同時,不對映 Id 屬性:

TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType)
    .Ignore("Id");

在下面這個例子中,對映配置只對 IQueryable 生效:

TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection)
    .IgnoreAttribute(typeof(NotMapAttribute));

只配置目標型別

在不確定源型別的時候,使用 ForDestinationType 來建立針對於 目標型別 的對映配置。

比如使用 AfterMapping 在對映完成後呼叫目標型別物件的 Validate 方法:

TypeAdapterConfig.GlobalSettings.ForDestinationType<IValidator>()
                    .AfterMapping(dest => dest.Validate());

注意!在上面的程式碼段中指定目標型別為 IValidator 介面,那麼將會把對映配置應用到所有實現了 IValidator 的型別。

泛型型別對映

如果對映的是泛型型別,可以透過將泛型型別傳給 ForType 來建立設定.

TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))
    .Map("value", "Value");

對映配置繼承

隱式繼承對映配置

源型別

Mapster 預設會把 源型別的 對映配置 應用到 源型別的子類。

如建立了一個 SimplePoco -> SimpleDto 的對映配置:

TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig()
    .Map(dest => dest.Name, src => src.Name + "_Suffix");

那麼繼承了 SimplePocoDerivedPoco 也將應用同樣的對映配置:

var dest = TypeAdapter.Adapt<DerivedPoco, SimpleDto>(src); 
//dest.Name = src.Name + "_Suffix"

如果不希望子類使用父類對映配置,可以設定 AllowImplicitSourceInheritancefalse 關閉繼承:

TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = false;

目標型別

Mapster 預設不會把 目標型別的 對映配置 應用到 目標型別的子類。

可以設定 AllowImplicitDestinationInheritance 開啟:

TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true;

顯示繼承對映配置

可以透過 Inherits 方法顯示的繼承型別對映配置:

TypeAdapterConfig<DerivedPoco, DerivedDto>.NewConfig()
        .Inherits<SimplePoco, SimpleDto>();

子類繼承父類對映配置

可以透過 Include 方法顯示的讓子類繼承父類的對映配置:

TypeAdapterConfig<Vehicle, VehicleDto>.NewConfig()
    .Include<Car, CarDto>();

Vehicle vehicle = new Car { Id = 1, Name = "Car", Make = "Toyota" };
var dto = vehicle.Adapt<Vehicle, VehicleDto>();

dto.ShouldBeOfType<CarDto>();
((CarDto)dto).Make.ShouldBe("Toyota"); // "Make" 屬性在 Vehicle 中不存在

配置例項

配置例項

在 Mapster 中,預設的配置例項為 TypeAdapterConfig.GlobalSettings ,如果需要在不同場景下有不同的對映配置,Mapster 提供了 TypeAdapterConfig 用於實現此需求:

var config = new TypeAdapterConfig();
config.Default.Ignore("Id");

如何給 配置例項 新增l型別對映配置?

直接使用 NewConfigForType 方法即可:

config.NewConfig<TSource, TDestination>()
        .Map(dest => dest.FullName,
            src => string.Format("{0} {1}", src.FirstName, src.LastName));

config.ForType<TSource, TDestination>()
        .Map(dest => dest.FullName,
            src => string.Format("{0} {1}", src.FirstName, src.LastName));

透過將 配置例項 作為引數傳給 Adapt 方法來應用特定的型別對映配置:

注意! 配置例項 在程式中一定要作為單例存在,否則會影響效能!

var result = src.Adapt<TDestination>(config);

也可以建立一個指定 TypeAdapterConfigMapper,使用 Mapper 來做對映:

var mapper = new Mapper(config);
var result = mapper.Map<TDestination>(src);

複製配置例項

如果想從現有的配置中建立配置例項,可以使用 Clone 方法。

例如 複製全域性配置例項 :

var newConfig = TypeAdapterConfig.GlobalSettings.Clone();

或者複製其它配置例項:

var newConfig = oldConfig.Clone();

Fork 配置

Fork 方法內部直接呼叫 Clone 方法,但是使用 Fork 方法的形式與使用 Clone 方法有些許差別。

Fork 方法透過傳入委託方法直接增加新的對映配置:

var forked = mainConfig.Fork(config => 
				{
					config.ForType<Poco, Dto>()
						.Map(dest => dest.code, src => src.Id);
				});

var dto = poco.Adapt<Dto>(forked);

以上程式碼等同於使用 Clone 方法實現的以下程式碼:

var forked = mainConfig.Clone();
forked.ForType<Poco, Dto>()
      .Map(dest => dest.code, src => src.Id);

var dto = poco.Adapt<Dto>(forked);

配置位置

入口

對映配置應該只初始化並且只進行一次配置。因此在編寫程式碼的時候不能將對映配置和對映呼叫放在同一個地方。

例如下面的例子,執行將會丟擲一個異常:

config.ForType<Poco, Dto>().Ignore("Id");
var dto1 = poco1.Adapt<Dto>(config);

config.ForType<Poco, Dto>().Ignore("Id"); //<--- 這裡將丟擲異常,因為在這之前已經觸發過了對映
var dto2 = poco2.Adapt<Dto>(config);

所以,在編寫程式碼時應該將對映配置和對映呼叫分開,將對映配置放到程式的入口,例如:

  • Main 方法
  • Global.asax.cs
  • Startup.cs
// Application_Start in Global.asax.cs
config.ForType<Poco, Dto>().Ignore("Id");
// in Controller class
var dto1 = poco1.Adapt<Dto>(config);
var dto2 = poco2.Adapt<Dto>(config);

保證配置例項和對映配置在同一位置

將配置例項和對映配置分開,可能會導致程式碼處於不同的位置,在程式碼編寫過程中可能會出現遺漏之類的問題。

使用 Fork 函式可以使配置例項與對映配置在同一位置:

var dto = poco.Adapt<Dto>(
	config.Fork(forked => forked.ForType<Poco, Dto>().Ignore("Id"));

不用擔心效能問題,只有第一次使用配置例項時會編譯,之後的呼叫將從快取中獲取。

在泛型類或方法中使用 Fork

Fork 方法預設使用檔名、行號作為鍵值。但是如果在在泛型類或泛型方法中呼叫Fork方法,必須指定鍵和型別全名稱,防止 Fork 從不同的型別引數返回無效的配置。

IQueryable<TDto> GetItems<TPoco, TDto>()
{
    var forked = config.Fork(
        f => f.ForType<TPoco, TDto>().Ignore("Id"), 
        $"MyKey|{typeof(TPoco).FullName}|{typeof(TDto).FullName}");
    return db.Set<TPoco>().ProjectToType<TDto>(forked);
}

不同的程式集

對映配置處於多個不同的程式集是比較常見的情況。

也許你的域程式集有一些對映到域物件的規則,而你的 web api 有一些特定的規則來對映到您的 api 約定。

Scan 方法

Mapster 支援掃描程式集註冊對映配置,可以幫助你快速註冊對映配置並減小忘記編碼註冊配置的機率。

在某些特定的情況下可以順序註冊程式集,以達到重寫對映配置的目的。

掃描程式集註冊對映配置非常簡單,只需要在程式集中建立一個或多個 IRegister 的實現,然後呼叫 TypeAdapterConfig 例項的 Scan 方法即可:

public class MyRegister : IRegister
{
	public void Register(TypeAdapterConfig config)
	{
		config.NewConfig<TSource, TDestination>();

		//OR to create or enhance an existing configuration
		config.ForType<TSource, TDestination>();
	}
}

使用全域性配置例項掃描程式集中的配置註冊器:

TypeAdapterConfig.GlobalSettings.Scan(assembly1, assembly2, assemblyN)

或使用特定的配置例項掃描程式集中的配置註冊器:

var config = new TypeAdapterConfig();
config.Scan(assembly1, assembly2, assemblyN);

Apply 方法

如果你使用的是其它 程式集掃描庫(如 MEF ), 那麼你可以呼叫配置例項的 Apply 方法將對映配置註冊器新增到配置例項中:

var registers = container.GetExports<IRegister>();
config.Apply(registers);

Apply 方法允許你選擇某些 對映配置註冊器 新增到 配置例項 中,而不是像 Scan 方法把程式集裡的所有 對映配置註冊器 新增到 配置例項 中:

var register = new MockingRegister();
config.Apply(register);

特性標籤

Mapster 支援透過為型別增加 特性標籤 的方式 新增型別對映配置:

[AdaptTo(typeof(StudentDto), PreserveReference = true)]
public class Student { 
    ...
}

對映配置驗證和編譯

為了在單元測試中驗證對映,並幫助處理“Fail Fast”情況,新增了以下嚴格對映模式。

顯式對映

透過修改 配置例項 RequireExplicitMapping 值決定是否開啟強制顯示對映。

RequireExplicitMapping 值為 true 時,所有型別對映必須顯式配置,即使非常簡單:

// 預設值為: "false"
TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true;
// 當你必須為每個類有一個顯式的配置,即使它只是:
TypeAdapterConfig<Source, Destination>.NewConfig();

檢查目標型別程式

透過修改 配置例項 RequireDestinationMemberSource 值決定是否開啟強制目標型別成員檢查。

RequireDestinationMemberSource 值為 true 時,所有 目標型別的欄位 必須與 源型別欄位 保持一致或顯示的指定對映或忽略成員:

// 預設值為: "false"
TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true;

驗證對映配置

呼叫 TypeAdapterConfig<Source, Destination>.NewConfg()Compile 方法將驗證 特定型別的對映配置是否存在錯誤;

呼叫 配置例項 的 Compile 方法以驗證 配置例項中的對映配置 是否存在錯誤;

另外,如果啟用了 顯式對映 , 它還將包含沒有在對映器中註冊的類的錯誤。

// 驗證特定配置
var config = TypeAdapterConfig<Source, Destination>.NewConfig();
config.Compile();

// 驗證整個配置例項的配置
TypeAdapterConfig<Source, Destination>.NewConfig();
TypeAdapterConfig<Source2, Destination2>.NewConfig();
TypeAdapterConfig.GlobalSettings.Compile();

配置編譯

Mapster 將在第一次呼叫對映時自動編譯:

var result = poco.Adapt<Dto>();

你也可以透過呼叫 配置例項 或 特定對映配置的Compile 方法編譯對映:

// 全域性配置例項
TypeAdapterConfig.GlobalSettings.Compile();

// 配置例項
var config = new TypeAdapterConfig();
config.Compile();

// 特定配置
var config = TypeAdapterConfig<Source, Destination>.NewConfig();
config.Compile();

推薦在程式新增對映配置完成後呼叫一次 Compile 方法,可以快速驗證 對映配置中是否存在錯誤,而不是在執行到某一行業務程式碼時觸發錯誤降低效率。

注意!呼叫 Compile 方法前應該完成所有的對映配置,呼叫 Compile 方法之後 配置例項 就不允許新增修改其它對映配置!

配置巢狀對映

例如有以下 父類、子類:

public class ParentPoco
{
    public string Id { get; set; }
    public List<ChildPoco> Children { get; set; }
    public string Name { get; set; }
}
public class ChildPoco
{
    public string Id { get; set; }
    public List<GrandChildPoco> GrandChildren { get; set; }
}
public class GrandChildPoco
{
    public string Id { get; set; }
}

如果你配置了父型別:

TypeAdapterConfig<ParentPoco, ParentDto>.NewConfig()
    .PreserveReference(true);

預設情況下,子型別不會從 PreserveReference 中得到效果。

因此必須在 ParentPoco 中指定所有型別對映:

TypeAdapterConfig<ParentPoco, ParentDto>.NewConfig()
    .PreserveReference(true);
TypeAdapterConfig<ChildPoco, ChildDto>.NewConfig()
    .PreserveReference(true);
TypeAdapterConfig<GrandChildPoco, GrandChildDto>.NewConfig()
    .PreserveReference(true);

或者可以呼叫 全域性配置例項 的 PreserveReference 方法:

TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true);

Fork

你可以使用 Fork 方法來定義僅將指定的對映應用於巢狀對映而不汙染全域性設定的配置:

TypeAdapterConfig<ParentPoco, ParentDto>.NewConfig()
    .Fork(config => config.Default.PreserveReference(true));

忽略為null或為空的字串

再比如,Mapster 只能忽略 null 值 (IgnoreNullValues),但是你可以使用 Fork 來忽略 null 或空值。

TypeAdapterConfig<ParentPoco, ParentDto>.NewConfig()
    .Fork(config => config.ForType<string, string>()
        .MapToTargetWith((src, dest) => string.IsNullOrEmpty(src) ? dest : src)
    );

Mapster.Tool

安裝 Mapster.Tool

# 如果已經擁有dotnet-tools.json,則跳過此步驟
dotnet new tool-manifest 

dotnet tool install Mapster.Tool

安裝 Mapster

簡單對映的情況下, 只需要安裝 Mapster.Core:

PM> Install-Package Mapster.Core

如果需要 TypeAdapterConfig 進行復雜對映配置,需要安裝 Mapster:

PM> Install-Package Mapster

命令列

Mapster.Tool 提供了3個命令

  • model: 從實體生成模型
  • extension: 從實體生成擴充套件方法
  • mapper: 從介面生成對映器

並且 Mapster.Tool 提供了以下選項

  • -a: 定義輸入程式集
  • -b: 指定用於生成動態輸出和名稱空間的基本名稱空間
  • -n: 定義生成類的名稱空間
  • -o: 定義輸出目錄
  • -p: 列印完整的型別名稱(如果Poco/Dto有相同的名稱)
  • -r: 生成record 型別而不是POCO型別

整合到csproj檔案

手動生成

將以下程式碼新增到 csproj 檔案中:

  <Target Name="Mapster">
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet build" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tool restore" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster model -a &quot;$(TargetDir)$(ProjectName).dll&quot;" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster extension -a &quot;$(TargetDir)$(ProjectName).dll&quot;" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster mapper -a &quot;$(TargetDir)$(ProjectName).dll&quot;" />
  </Target>

csproj 檔案目錄下生成如下命令:

dotnet msbuild -t:Mapster

在Build時自動生成

將以下程式碼新增到 csproj 檔案中:

  <Target Name="Mapster" AfterTargets="AfterBuild">
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tool restore" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster model -a &quot;$(TargetDir)$(ProjectName).dll&quot;" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster extension -a &quot;$(TargetDir)$(ProjectName).dll&quot;" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster mapper -a &quot;$(TargetDir)$(ProjectName).dll&quot;" />
  </Target>

清理

將以下程式碼新增到 csproj 檔案中:

  <ItemGroup>
    <Generated Include="**\*.g.cs" />
  </ItemGroup>
  <Target Name="CleanGenerated">
    <Delete Files="@(Generated)" />
  </Target>

清理命令如下:

dotnet msbuild -t:CleanGenerated

生成完整型別名

如果POCOs和dto有相同的名稱,您可能需要使用完整的型別名稱來生成,透過 -p 選項:

  <Target Name="Mapster">
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet build" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet tool restore" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster model -a &quot;$(TargetDir)$(ProjectName).dll&quot; -p" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster extension -a &quot;$(TargetDir)$(ProjectName).dll&quot; -p" />
    <Exec WorkingDirectory="$(ProjectDir)" Command="dotnet mapster mapper -a &quot;$(TargetDir)$(ProjectName).dll&quot; -p" />
  </Target>

動態輸出和名稱空間

例如,存在以下結構:

Sample.CodeGen
- Domains
  - Sub1
    - Domain1
  - Sub2
    - Domain2

如果將基本名稱空間指定為 Sample.CodeGen.Domains:

<Exec WorkingDirectory="$(ProjectDir)" 
    Command="dotnet mapster model -a &quot;$(TargetDir)$(ProjectName).dll&quot; -n Sample.CodeGen.Generated -b Sample.CodeGen.Domains" />

程式碼將生成到:

Sample.CodeGen
- Generated
  - Sub1
    - Dto1
  - Sub2
    - Dto2

生成Dto和對映程式碼

有3種方式來生成dto和對映程式碼

  • Fluent API: 如果不想編輯實體類定義,或者從不同程式集中的實體型別生成dto。
  • Attributes: 如果想保持對映配置到實體類。
  • Interfaces: 如果已經有dto,並且想透過介面定義對映。

Sample

  • https://github.com/MapsterMapper/Mapster/tree/master/src/Sample.CodeGen

相關文章