1. 程式人生 > 實用技巧 >物件之間的對映(AutoMapper整合)

物件之間的對映(AutoMapper整合)

2.7 ABP公共結構 - 物件之間的對映

2.7.1 簡介

我們通常需要在近似的物件之間進行對映處理。這是一個重複且枯燥無味的工作,通常來說兩個需要相互對映的物件之間有近似的或者相同的屬性。思考一下這樣一個案例:應用服務的方法:

  1. public class UserAppService : ApplicationService
  2. {
  3. private readonly IRepository<User> _userRepository;
  4. public UserAppService(IRepository<User> userRepository)
  5. {
  6. _userRepository = userRepository;
  7. }
  8. public void CreateUser(CreateUserInput input)
  9. {
  10. var user = new User
  11. {
  12. Name = input.Name,
  13. Surname = input.Surname,
  14. EmailAddress = input.EmailAddress,
  15. Password = input.Password
  16. };
  17. _userRepository.Insert(user);
  18. }
  19. }

在這裡,User是一個簡單的

實體;CreateUserInput是一個簡單的DTO。從給定的輸入物件,我們需要使用它來建立一個User實體。在真實的環境中User實體會有更多的屬性,手動建立這個實體會變得枯燥無味且易出錯。如果我們想要新增新的屬性到User和CreateUserInput的時候,這會變得很複雜,我們應該改變這個對映程式碼,使對映更簡單。

我們可以使用一個類庫來實現自動對映。AutoMapper是最好的處理物件到物件之間對映的類庫之一。ABP中定義了 IObjectMapper 介面,抽象了對映功能。在Abp.AutoMapper包中,我們實現了該介面來使用AutoMapper。

2.7.2 IObjectMapper 介面

IObjectMapper簡單的抽象出了物件到物件之間對映的方法。我們可以使用更簡單的程式碼實現上面提到的對映功能:

  1. public class UserAppService : ApplicationService
  2. {
  3. private readonly IRepository<User> _userRepository;
  4. private readonly IObjectMapper _objectMapper;
  5. public UserAppService(IRepository<User> userRepository, IObjectMapper objectMapper)
  6. {
  7. _userRepository = userRepository;
  8. _objectMapper = objectMapper;
  9. }
  10. public void CreateUser(CreateUserInput input)
  11. {
  12. var user = _objectMapper.Map<User>(input);
  13. _userRepository.Insert(user);
  14. }
  15. }

Map 是一個簡單的具有型別宣告的泛型佔位符的方法,可以將一個物件對映為另一個物件。Map方法的過載方法可以對映一個物件到一個 已存在 的物件。假設我們有了一個User實體,但是我們想通過DTO來更新使用者實體的某些屬性:

  1. public void UpdateUser(UpdateUserInput input)
  2. {
  3. var user = _userRepository.Get(input.Id);
  4. _objectMapper.Map(input, user);
  5. }

2.7.3 AutoMapper 整合

Abp.AutoMapper包中,我們實現了IObjectMapper介面並提供了一些輔助功能。

安裝

首先,需要安裝 Abp.AutoMapper 到你的專案中:

  1. Install-Package Abp.AutoMapper

然後新增 AbpAutoMapperModule 作為依賴項到你定義的模組類中:

  1. [DependsOn(typeof(AbpAutoMapperModule))]
  2. public class MyModule : AbpModule
  3. {
  4. ...
  5. }

這樣你就可以在程式碼中安全的注入和使用IObjectMapper介面了。如果有需要,你也可以使用AutoMapper自己的API。

建立對映

在使用對映之前,AutoMapper預設需要定義類之間的對映關係。在使用的時候你可以查詢它的文件。但是使用ABP會使對映關係的建立更簡單且模組化。

自動對映特性

大多數時候你只想對類進行直接(按約定的方式)對映。在這種情況下,你可以使用 AutoMap,AutoMapFrom 以及 AutoMapTo 特性。例如:在上面的例子中,我們將 CreateUserInput 對映到 User,我們可以使用 AutoMapTo 特性來實現。如下所示:

  1. [AutoMapTo(typeof(User))]
  2. public class CreateUserInput
  3. {
  4. public string Name { get; set; }
  5. public string Surname { get; set; }
  6. public string EmailAddress { get; set; }
  7. public string Password { get; set; }
  8. }

AutoMap特性可以在兩個型別之間實現彼此之間的相互對映。但是在這個例子中,我們只需要將 CreateUserInput 對映到 User。所以我們可以使用 AutoMapTo

自定義對映

在某些情況下,簡單的對映不能滿足需求。例如:兩個類中的屬性名字可能稍微有些不同或者你想忽略某些屬性的對映。在這種情況下,你可以直接使用 AutoMapper 的 API 來實現對映。Abp.AutoMapper 包中的定義的 API 使自定義對映更加模組化。

假設在對映的時候,我們想忽略Password屬性,並使 EmailAddress 屬性對映到 User 的Email 屬性。我們可以像下面一樣來實現對映關係:

  1. [DependsOn(typeof(AbpAutoMapperModule))]
  2. public class MyModule : AbpModule
  3. {
  4. public override void PreInitialize()
  5. {
  6. Configuration.Modules.AbpAutoMapper().Configurators.Add(config =>
  7. {
  8. config.CreateMap<CreateUserInput, User>()
  9. .ForMember(u => u.Password, options => options.Ignore())
  10. .ForMember(u => u.Email, options => options.MapFrom(input => input.EmailAddress));
  11. });
  12. }
  13. }

AutoMapper擁有更多的選項和能力來做物件之間的對映。詳情請查詢文件

MapTo擴充套件方法

如上面所述,我們建議注入並使用IObjectMapper介面。這使我們的專案儘可能的不依賴AutoMapper。這也使單元測試更簡單,因為在單元測試的時候我們可以替換掉對映依賴。

在 Abp.AutoMapper 中也有 MapTo 的擴充套件方法,我們可以不注入IObjectMapper介面,使用它將任意物件對映為其它物件。如下所示:

  1. public class UserAppService : ApplicationService
  2. {
  3. private readonly IRepository<User> _userRepository;
  4. public UserAppService(IRepository<User> userRepository)
  5. {
  6. _userRepository = userRepository;
  7. }
  8. public void CreateUser(CreateUserInput input)
  9. {
  10. var user = input.MapTo<User>();
  11. _userRepository.Insert(user);
  12. }
  13. public void UpdateUser(UpdateUserInput input)
  14. {
  15. var user = _userRepository.Get(input.Id);
  16. input.MapTo(user);
  17. }
  18. }

在 Abp.AutoMapper 的名稱空間中定義了 MapTo 的擴充套件方法。首先你得在你的程式碼中匯入該名稱空間。

由於MapTo擴充套件方法是靜態的,它使用的是AutoMapper的靜態例項。對於應用程式程式碼這是簡單且有效的,但是靜態配置在單元測試的時候會有問題,因為在單元測試的時候,會在各個單元測試之間共享對映關係。

單元測試

我們想隔離各個測試單元。為了實現該功能,我們應該使我們的專案遵循下面規則:

    1. 使用IObjectMapper介面,而不使用MapTo靜態方法。
    1. 配置 Abp.AutoMapper 模組使用本地Mapper例項(在依賴注入時,註冊為單例模式)而不是靜態例項(Abp.AutoMapper 預設使用靜態 Mapper.Instance例項,這樣我們就可以使用MapTo擴充套件方法來實現物件之間的對映,如上面例子所示)。
  1. Configuration.Modules.AbpAutoMapper().UseStaticMapper = false;

預定義對映

LocalizableString -> string

Abp.AutoMapper模組中定義了一個對映方法:將LocalizableString(或者ILocalizableString)物件轉換為string物件。它使用ILocalizationManager介面來實現轉換。所以在處理任意類的對映時,可本地化屬性被自動的本地化。

注入IMapper

你可以直接使用AutoMapper的IMapper物件,而不是使用IObjectMapper介面。這樣的話,在你的類中注入IMapper並使用它。Abp.AutoMapper包註冊IMapper作為依賴注入單例。