天天看點

ABP文檔 - 對象與對象之間的映射

文檔目錄

本節内容:

  • 簡介
  • 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有更多的選項和功能來映射對象,你可以檢視它的文檔了解更多。

擴充方法 MapTo

建議注入和使用前面說的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;      

Abp.AutoMapper子產品定義了一個從LocalizableString (或 ILocalizableString) 對象到string對象的映射,它使用ILoclaizationManager進行轉換,是以在映射過程中,可以本地化的屬性會自動的本地化。

如果你需要注入AutoMapper的IMapper對象來代替IObjectMapper,就直接在你的類裡注入IMapper并使用它,Abp.AutoMapper包把IMapper作為單例注冊到了依賴注入系統裡。

kid1412聲明:轉載請把此段聲明完整地置于文章頁面明顯處,并保留個人在部落格園的連結:http://www.cnblogs.com/kid1412/(可點選跳轉)。

繼續閱讀