如何在.NET中實現物件資料對映

語言: CN / TW / HK

theme: cyanosis highlight: vs2015


  • 持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第9天,點選檢視活動詳情

前言

物件資料對映即將一個物件的資料根據特定規則批量對映到另一個物件中,減少手工操作和降低人為出錯率。如將 DTO 物件和 Entity 實體相互轉換對映。

示例

在我們平常表單提交中,我們通常會定義一個DTO讓使用者填寫一些必須的資訊而並不是將資料庫所有的欄位羅列讓使用者填寫,在過去我們需要如何操作: // 資料庫User表 public class User { public int UserId { get; set; } // 使用者編號 public string UserName { get; set; } // 使用者名稱稱 public int Age { get; set; } // 年齡 public DateTime? CreateAt { get; set; } // 建立時間 public int CreateBy { get; set; } // 建立人 public DateTime Birthday { get; set; } // 生日 } 如上資料庫表設計,我們使用者編號、建立時間、建立人、包括年齡都是系統計算或者系統生成的,可能提供給使用者填寫的資料只有名稱和生日: public class UserRequestDto { public string UserName { get; set; } public DateTime Birthday { get; set; } } 在以前我們應該這樣處理 public async Task Create(UserRequestDto request) { // 例項化一個User實體,並且將使用者填寫內容一個一個賦值 User user = new User(); user.UserName = request.UserName; user.Birthday = request.Birthday; user.CreateAt = DateTime.Now; .... // 建立使用者 await context.User.InsertAsync(user); }

問題: 如果很多地方需要這樣的賦值操作,那麼將有非常多的程式碼冗餘,而且如果欄位過多非常容易出錯,操作效率極低。

有了如上問題,我們實現自動對映的需求就出現了,在C#中有比較優秀的物件對映工具 MapsterAutoMapper,據說 Mapster 使用簡單且效能高。

Mapster 使用

Mapster 是一個使用簡單,功能強大,效能極佳的物件對映框架。與 AutoMapper 相比在速度和記憶體佔用方面表現更加優秀,可以在只使用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

如上為官方提供的效能測試表格,當然還是根據個人喜好選擇,具體測試結果也僅供參考,大家也可以自行研究選擇。

image.png

  • 對映到一個新的物件 // 一行程式碼搞定,就是這麼神奇 User user = request.Adapt<User>();

image.png

  • 在EFCore中使用 (Mapster 提供了對 IQueryable 的對映擴充套件)

在EFCore中查詢所需要的格式我們通常使用Select實現 context.User.Select(x => new UserDto { UserName = x.UserName, Age = x.Age ... ... }) 使用 ProjectToType 對映到目標型別 var result = context.User.ProjectToType<UserDto>().ToList();

  • 自定義對映

在某些特殊情況下當我們源屬性型別和目標屬性名稱不對應的時候我們可以進行自定義對映關係 // 在資料對映時,將出生日期通過計算方法對映給返回的Age TypeAdapterConfig<User, UserDto> .NewConfig() .Ignore("Id")//指定忽略某些欄位 .Map(dest => dest.Age, src => CalcAge(src.Birthday));

  • 在某些情況下,如果需要在 依賴注入(DI)使用,Mapster提供了IMapperandMapper ``` public void ConfigureServices(IServiceCollection services) { var config = new TypeAdapterConfig(); services.AddSingleton(config);//使用單例註冊 services.AddScoped();//註冊服務 }

// Service進行依賴注入 private readonly IMapper _mapper; public UserService(IMapper mapper) { _mapper = mapper; }

public void Create(UserRequestDto request) { // 使用服務 var user = _mapper.Map(request); } - 資料型別轉化 decimal i = 123.Adapt();// int轉換成decimal

var e = "Read, Write, Delete".Adapt(); // 列舉 ```

總結

使用 Mapster 能讓我們在處理尤其是 Entity 與 DTO 之間資料相互對映,如果手動對映會導致效率差,程式碼冗餘, Mapster的優勢還是非常明顯的,當然也不是說 AutoMapper 就非常拉跨,大家根據自己的需求選擇即可。