如何在 .NET 使用 AutoMapper
TLDR
- AutoMapper 用於簡化分層架構中 DTO 與 Entity 之間繁瑣的屬性賦值。
MapperConfiguration應維持單例,建議在應用程式啟動時(如Program.cs)完成設定。- 務必使用
config.AssertConfigurationIsValid()驗證映射規則,確保 Destination 的成員皆有對應來源。 - 使用
Profile類別可將映射邏輯模組化,提升程式碼可維護性。 - 針對複雜邏輯,可透過
ForMember進行條件映射、忽略欄位或自訂轉換規則。 - 使用
ReverseMap()可簡化雙向映射,但複雜轉換需額外搭配ForPath定義反向邏輯。 - 建議搭配
AutoMapper.Extensions.Microsoft.DependencyInjection套件,透過 DI 容器自動註冊映射設定。
AutoMapper 的核心使用方式
在分層架構中,為了實現關注點分離,常需在 Entity、Service DTO 與 ViewModel 之間進行轉換。AutoMapper 透過 Reflection 簡化這些屬性賦值操作。
基本設定流程
什麼情況下會遇到這個問題:當需要初始化映射規則並確保其正確性時。
csharp
// 建立 Configuration 設定 class 之間的映射關係
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Order, OrderDto>();
});
// 驗證 Configuration 的設置,若 Destination 有成員未被對應,將拋出 AutoMapperConfigurationException
config.AssertConfigurationIsValid();
// 建立 Mapper
var mapper = config.CreateMapper();
// 將 source 的值映射至新的 Destination 物件
Destination dest = mapper.Map<Destination>(source);
// 將 source 的值映射至已存在的 dest 物件
mapper.Map(source, dest);映射規則設定
常用映射與 Profile 組織
什麼情況下會遇到這個問題:當專案有多個類別需要映射,且希望將設定集中管理時。
透過繼承 Profile 類別,可以將映射邏輯拆分,並透過 AddProfile 引用:
csharp
public class OtherProfile : Profile {
public OtherProfile() {
// 設定 Source 可轉換成 Destination
CreateMap<Source, Destination>();
// 使用 ConvertUsing 定義全域轉換邏輯(例如:字串去空白)
CreateMap<string?, string?>().ConvertUsing(x => x == null ? x : x.Trim());
}
}處理屬性前/後綴詞
什麼情況下會遇到這個問題:當 Source 與 Destination 的屬性命名規則不一致(例如包含 Prefix 或 Postfix)時。
csharp
var config = new MapperConfiguration(cfg => {
cfg.RecognizePrefixes("Prefix");
cfg.RecognizePostfixes("Postfix");
cfg.RecognizeDestinationPrefixes("Prefix");
cfg.RecognizeDestinationPostfixes("Postfix");
// 若不希望使用預設的 Get 前綴詞
cfg.ClearPrefixes();
});針對單一 Member 的進階設定
什麼情況下會遇到這個問題:當需要處理特殊邏輯,如忽略欄位、條件映射或預設值處理時。
csharp
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Source, Destination>()
.ForMember(desc => desc.Prop1, opt => opt.Ignore()) // 忽略特定欄位
.ForMember(dest => dest.DestProp2, opt => opt.MapFrom(src => src.SourceProp2)) // 指定對應欄位
.ForMember(desc => desc.DateProp3, opt => opt.MapFrom(src => DateTime.Now)) // 固定值或計算值
.ForMember(dest => dest.IntProp4, opt => opt.Condition(src => (src.IntProp4 >= 0))) // 條件映射
.ForMember(dest => dest.Prop5, opt => opt.NullSubstitute("Other Value")); // Null 替代值
});反向映射 (ReverseMap)
什麼情況下會遇到這個問題:當需要實現雙向轉換,且不想重複撰寫兩次 CreateMap 時。
使用 ReverseMap() 可快速建立反向規則。需注意,複雜的轉換需配合 ForPath 定義反向邏輯,且 AssertConfigurationIsValid() 對反向映射無效。
csharp
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Prop5, opt => opt.MapFrom(src => src.Prop3 + "," + src.Prop4))
.ReverseMap()
.ForPath(s => s.Prop3, opt => opt.MapFrom(src => src.Prop5.Split(new char[] { ',' })[0]));
});Dependency Injection 整合
什麼情況下會遇到這個問題:在 .NET Core 或 .NET 6+ 環境中,希望透過 DI 容器自動管理 Mapper 生命週期時。
需安裝 AutoMapper.Extensions.Microsoft.DependencyInjection 套件。
csharp
// 在 Program.cs 或 Startup.cs 中註冊
builder.Services.AddAutoMapper(typeof(ProfileTypeFromAssembly1), typeof(ProfileTypeFromAssembly2));
// 在 Controller 或 Service 中注入使用
public class EmployeesController {
private readonly IMapper mapper;
public EmployeesController(IMapper mapper) => this.mapper = mapper;
}異動歷程
- 初版文件建立。