使用 AutoMapper 簡化 C# 程式碼

在物件導向的世界中,每一個階層的物件都有其定義、屬性與權限規範,因此在我們程式中常有物件與物件間的轉換。

在不使用今天介紹的工具之前,我們的程式碼很常會是這個樣子

1
2
3
4
5
6
7
8
9
10
var personModel = new PersonModel
{
Id = personDto.Id,
EMail = personDto.EMail,
Name = personDto.Name,
Gender = personDto.Gender,
Remark = personDto.Remark
};

restPersonService.DoOtherLogic(personModel);

這個東西會隨著每個階層都來一次,像是 service 層、repository 層等,會造成以下困擾:

  • 程式碼又臭又長
  • 阻礙閱讀
  • 模糊焦點,邏輯難查
  • 重複程式碼超多
  • 新增一個成員變數會改到死,容易遺漏

如果你的程式碼、工作的專案有類似狀況,那 AutoMapper 這個工具將非常適合你。


測試範例

以下都會用這兩個類別來做測試的範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class PersonDto
{
public string Id { get; set; }

public string FirstName { get; set; }

public string LastName { get; set; }

public string Remark { get; set; }
}

public class PersonModel
{
public string Id { get; set; }

public string Remark { get; set; }

public string FullName { get; set; }
}

在 DotNetCore 安裝

直接在 Nuget 上找到&安裝

  • AutoMapper.Extensions.Microsoft.DependencyInjection

註冊

AutoMapper 需要設定檔來定義哪些物件之間轉換的關係,左邊是來源、右邊是轉換的目標物件 PersonDto -> PersonModel

1
2
3
4
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<PersonDto, PersonModel>();
});

如果你有雙向互轉需求,也可以在後面加上 .ReverseMap()

ex: cfg.CreateMap<PersonDto, PersonModel>().ReverseMap();

使用

1
2
3
var mapper = mapperConfig.CreateMapper();

var personModel = mapper.Map<PersonModel>(personDto);

宣告出 AutoMapper 物件之後,一行就能搞定,是不是很簡潔!

當然,我們實務上也不僅有單一物件需要轉換,陣列或 List 也是可以的喔!

1
var personModels = mapper.Map<PersonModel[]>(new []{ personDto });

C# 除非有元素增減操作,不然我一率是推薦使用陣列

物件欄位不同怎麼辦

實務上絕對是這種狀況的,例如上面的例子就是 Model 有 FullName 欄位,但是來源的 Dto 卻只有 FirstNameLastName

這時候要使用 .ForMember 設定:

1
2
cfg.CreateMap<PersonDto, PersonModel>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"));

透過 ForMember 指定 FullName 是由 FirstName 與 LastName 組合而成。

建立設定檔,一勞永逸

隨著我們設定轉換的物件越來越多,總不可能都堆在邏輯層吧?

於是透過建立 Profile 的方式,將轉換用的設定檔歸類在應該要有的位置,方法很簡單,建立一個新的類別並繼承 Profie

1
2
3
4
5
6
7
public class PersonProfile : Profile
{
public PersonProfile()
{
CreateMap<PersonDto, PersonModel>();
}
}

在宣告的地方加入即可,如果 Profiles 多的話,也可以考慮用 Assembly 的方式。

1
2
3
4
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new PersonProfile());
});

本人,強烈建議,不要在 AutoMapper 設定中:

  1. 寫邏輯
  2. 過度使用新的邏輯語法 Conditions, PreConditions, NullSubstitute

很多新語法看似很方便,但會對 Trace code / Debug 造成極大的困擾,絕對是痛過才知道。

這個工具的目的是讓我們重新對焦在邏輯處,把邏輯分散就不是好事!


Reference

  • 作者: MingYi Chou
  • 版權聲明: 轉載不用問,但請註明出處!本網誌均採用 BY-NC-SA 許可協議。