What is AutoMapper
“AutoMapper is an object-object mapper “,也就是能夠幫你將兩個不同型別的物件做快速的轉換,只要透過事先設定好的映射方式,就能很方便的使用並簡化程式碼。
Why use AutoMapper
當你的專案中,有大量且重複的類型轉換邏輯需求,換句話說,若有兩Class ,“userDto” 與 “userEntity" 之間常常需要單向或雙向的 map,且單一方向的map 方式皆固定,則 AutoMapper 就是一個合適的工具選擇。
反之,若你的需求中,userDto 與 userEntity 之間的 map 常因功能不同需要有不同的 map 邏輯,則你需要多拆出幾種對應的 class 來滿足所有 map 的需求,否則AutoMapper 就不適合你的專案。
According to the author of .Net AutoMapper , Jimmy Bogard
AutoMapper works because it enforces a convention. It assumes that your destination types are a subset of the source type. It assumes that everything on your destination type is meant to be mapped. It assumes that the destination member names follow the exact name of the source type. It assumes that you want to flatten complex models into simple ones.
All of these assumptions come from our original use case — view models for MVC, where all of those assumptions are in line with our view model design. With AutoMapper, we could enforce our view model design philosophy. This is the true power of conventions — laying down a set of enforceable design rules that help you streamline development along the way.
How to use AutoMapper
第一步 : 安裝 AutoMapper
classes:npm i @automapper/core @automapper/classes reflect-metadata #classes
pojos:npm i @automapper/core @automapper/pojos #pojos
這邊可以依照開發方法,選擇 classes 或 pojos 其中一種 strategy 來安裝,
本篇將使用 classes ,pojos 的用法可以參考 這裡
第二步 : 宣告 class
此處 “@AutoMap” 的 decorator 是用來定義哪些資料會被 map,並且在括號內宣告該變數的 metadata給 AutoMpper,使 AutoMapper 在 Map 時能判斷正確型別。
宣告完成後 AutoMapper 會自動去尋找變數名稱相同的資料做Map
* ./entity/UserInfo.entity.tsimport { AutoMap } from '@automapper/classes';
export class UserInfo {
@AutoMap(() => String)
userName: string; @AutoMap(() => String)
userId: string; @AutoMap(() => String)
phoneNumber: string; @AutoMap(() => Date)
birthDay: Date = new Date(); @AutoMap(() => Address)
address: Address; @AutoMap(() => Date)
registrationDate: Date = new Date(); //password 不需 map 所以不添加 @AutoMap
password: string;
}export class Address {
@AutoMap(() => String)
city: string; @AutoMap(() => String)
street: string; @AutoMap(() => String)
number: string;
}
* ./dto/UserInfo.dto.tsimport { AutoMap } from '@automapper/classes';
export class UserInfoDto {
@AutoMap(() => String)
userName: string; @AutoMap(() => String)
userId: string; @AutoMap(() => String)
phoneNumber: string; @AutoMap(() => String)
birthDay: string; @AutoMap(() => AddressDto)
address: AddressDto; @AutoMap(() => String)
registrationDate: string;
}export class AddressDto {
@AutoMap(() => String)
city: string; @AutoMap(() => String)
street: string; @AutoMap(() => String)
number: string;
}
第三步 : 設定 AutoMapper
*./configs/mapperConfigs.tsimport {createMapper,createMap,typeConverter}from‘@automapper/core’;
import { classes } from ‘@automapper/classes’;
import { Address, UserInfo } from ‘../entity/UserInfo.entity’;
import { AddressDto, UserInfoDto } from ‘../dto/UserInfo.dto’;
import moment from ‘moment’;//宣告 mapper 採用的 strategy
export const mapper = createMapper({ strategyInitializer: classes() });//建立 Address -> AddressDto 的 map關係
createMap(mapper, Address, AddressDto);
//建立 UserInfo -> UserInfoDto 的 map關係
createMap(
mapper,
UserInfo,
UserInfoDto,
// typeConverter : 只要 source 對應到 destination 的資料中有 Date
// -> String 的關係,就使用後面定義的 function 做轉換
typeConverter(Date, String, (date) => moment(date).format(‘YYYY-
MM-DD’))
);
第四步 : 執行 (測試使用 ts-node 執行)
* main.tsimport { mapper } from './config/MapperConfig';
import { UserInfoDto } from './dto/UserInfoDto';
import { UserInfo } from './entity/UserInfo';// 建立 UserInfo
const user: UserInfo = new UserInfo();
user.userName = 'Jack';
user.userId = '123456';
user.phoneNumber = '0912345678';
user.birthDay = new Date('1999-01-01 10:12:12');
user.address = { city: 'Taipei', street: 'First', number: '1' };
user.registrationDate = new Date('2020-01-01 10:12:12');
user.password = '345678';
const userInfoDto = mapper.map(user, UserInfo, UserInfoDto);// 使用 automapper 進行型態轉換
const userInfoDto : UserInfoDto = mapper.map(dto, UserInfo, UserInfoDto);console.log(userInfoDto);
output :
------------------------------
UserInfoDto {
registrationDate: '2020-01-01',
address: AddressDto { number: '1',street: 'First',city: 'Taipei'},
birthDay: '1999-01-01',
phoneNumber: '0912345678',
userId: '123456',
userName: 'Jack'
}
Summary
AutoMapper 出現是為了MVC 架構中 view-models 和 entity-models 之間的轉換,因為對應型別的 map 方式較為單純且固定,所以 AutoMapper 能很好的解決問題。
並且 AutoMapper 需要事先設定才能正確地 map,若有複雜的 map 需求則需要複雜的設定,但如果有大量的 map 都需要複雜的設定,則可能要回頭考慮資料結構是不是適當,或許有更好的結構能省下設定的時間。
上述是最基礎的 AutoMapper 使用,AutoMapper 還提供了許多 mapping configuration 可以設定,例如 forMember() 可對特定欄位做處理後再 map 的、 namingConventions() 可依據名稱解析複合資料結構並 map 出資料的 等等,會在下一篇再做說明。