文檔目錄 本節內容: 簡介 IObjectMapper 介面 集成 AutoMapper 安裝 創建映射 自動映射的特性 自定義映射 擴展方法 MapTo 單元測試 預定義的映射 LocalizableString -> string 註入 IMapper 安裝 創建映射 自動映射的特性 自定義映射 ...
本節內容:
把一個對象映射到另一個相似的對象很常見,兩個對象(類)具有相似或相同的屬性,它們之間要互相映射,其實這項工作重覆且無聊,考慮一個典型的應用服務方法,如下:
public class UserAppService : ApplicationService { private readonly IRepository<User> _userRepository; public UserAppService(IRepository<User> userRepository) { _userRepository = userRepository; } public void CreateUser(CreateUserInput input) { var user = new User { Name = input.Name, Surname = input.Surname, EmailAddress = input.EmailAddress, Password = input.Password }; _userRepository.Insert(user); } }
CreateUserInput是一個簡單的數據傳輸對象,User是一個簡單的實體,我們根據傳入的input手工創建一個User實體,現實應用里User實體將會有更多的屬性,手工創建它就會變得很無聊且容易出錯,而且,當我們添加新的屬性到User和CreateUserInput時,又需要修改映射的代碼。
其實我們可以用一個類庫來自動完成映射,AutoMapper是一個最好的對象到對象的映射類庫,Abp中定義了IObjectMapper介面來抽象它,且在 Abp.AutoMapper包中實現了這個介面。
IObjectMapper是一個簡單的包含把一個對象映射到另一個對象的方法的抽象,我們可以用如下代碼書寫上例:
public class UserAppService : ApplicationService { private readonly IRepository<User> _userRepository; private readonly IObjectMapper _objectMapper; public UserAppService(IRepository<User> userRepository, IObjectMapper objectMapper) { _userRepository = userRepository; _objectMapper = objectMapper; } public void CreateUser(CreateUserInput input) { var user = _objectMapper.Map<User>(input); _userRepository.Insert(user); } }
Map是一個簡單的方法,它獲取源對象並且根據泛型參數給定的類型創建一個對應的新的目標對象(此示例中的User),Map有一個重載版本,它把一個對象映射到一個已存在的對象,假設我們已經有一個User實體,想根據另一個對象更新這個實體的屬性:
public void UpdateUser(UpdateUserInput input) { var user = _userRepository.Get(input.Id); _objectMapper.Map(input, user); }
Abp.AutoMapper nuget包(模塊)實現了IObjectMapper介面並且提供了額外功能。
首先,安裝Abp.AutoMapper nuget包到你的項目里:
Install-Package Abp.AutoMapper
然後,在你的模塊上方添加對AbpAutoMapperModule 的依賴:
[DependsOn(typeof(AbpAutoMapperModule))] public class MyModule : AbpModule { ... }
接下來,你就可以在你代碼里放心地註入和使用IObjectMapper,當然,你如有需要,也可以使用 AutoMapper自身API。
AutoMapper要求在映射前,先定義兩個類之間的映射關係,你可以查閱一下它的文檔以瞭解更多詳情,Abp把它變得更簡單和模塊化
大部分情況下,你只想要直接(並且按約定)映射類,這種情況下,你可以使用AutoMap,AutoMapFrom和AutoMapTo特性。例如,當我們想映射上例中的CreateUserInput到User類,我們可以像如下所示的AutoMapTo特性:
[AutoMapTo(typeof(User))] public class CreateUserInput { public string Name { get; set; } public string Surname { get; set; } public string EmailAddress { get; set; } public string Password { get; set; } }
AutoMap特性在兩個類之間雙向映射,但在這個示例里,我們只需要從CreateUserInput映射到User,所以我們只需要用AutoMapTo.
簡單地映射可能不適用於一些場景,如,兩個類的屬性名稱有些不同或你可能想要在映射過程中忽略一些屬性,這些情況下可以直接使用AutoMapper的Api來自定義映射關係,不過Abp.AutoMapper包定義了更模塊化的Api.
假設我們想要忽略Password並把用EmailAddress映射到User的Email,我們可以作如下的定義:
[DependsOn(typeof(AbpAutoMapperModule))] public class MyModule : AbpModule { public override void PreInitialize() { Configuration.Modules.AbpAutoMapper().Configurators.Add(config => { config.CreateMap<CreateUserInput, User>() .ForMember(u => u.Password, options => options.Ignore()) .ForMember(u => u.Email, options => options.MapFrom(input => input.EmailAddress)); }); } }
AutoMapper有更多的選項和功能來映射對象,你可以查看它的文檔瞭解更多。
建議註入和使用前面說的IObjectMapper介面,因為它使我們的項目儘可能地與AutoMapper解藕,並且使單元測試更加容易,因為我們可以在單元測試里替換(模擬)映射。
Abp.AutoMapper模塊里同樣定義了MapTo這個擴展方法,它可以在不註入IObjectMapper的情況下把一個對象映射到另一個對象,例如:
public class UserAppService : ApplicationService { private readonly IRepository<User> _userRepository; public UserAppService(IRepository<User> userRepository) { _userRepository = userRepository; } public void CreateUser(CreateUserInput input) { var user = input.MapTo<User>(); _userRepository.Insert(user); } public void UpdateUser(UpdateUserInput input) { var user = _userRepository.Get(input.Id); input.MapTo(user); } }
MapTo擴展方法定義在 Abp.AutoMapper 命名空間里,所以你需要先在你的代碼里引入命名空間。
由於MapTo擴展方法是靜態的,它們使用AutoMapper的靜態實例(Mapper.Instance),這樣對於應用代碼來說,比較簡單和好用,但是在單元測試里由於靜態配置和映射是在不同的測試之間共用它,它們相互影響 ,可能會帶來一些問題。
我們想把每個單元測試獨立開來,所以我們需要為我們的項目定義如下規則:
1.只使用IObjectMapper,不使用擴展方法MapTo.
2.配置Abp.AutoMapper 模塊,使用局部的Mapper實例(用單例的方式註冊到依賴註入)不用靜態的(Abp.AutoMapper預設情況下,使用靜態的Mapper.Instance,從而可以像上面那樣使用MapTo擴展方法):
Configuration.Modules.AbpAutoMapper().UseStaticMapper = false;
LocalizableString -> string
Abp.AutoMapper模塊定義了一個從LocalizableString (或 ILocalizableString) 對象到string對象的映射,它使用ILoclaizationManager進行轉換,所以在映射過程中,可以本地化的屬性會自動的本地化。
如果你需要註入AutoMapper的IMapper對象來代替IObjectMapper,就直接在你的類里註入IMapper並使用它,Abp.AutoMapper包把IMapper作為單例註冊到了依賴註入系統里。