深入淺出-對象到對象映射

將對象映射到另一個對象是常用並且繁瑣重複的工作,大部分情況下兩個類都具有相同或相似的屬性. 例如下面的 應用服務方法:

public class UserAppService : ApplicationService
{
    private readonly IRepository<User, Guid> _userRepository;

    public UserAppService(IRepository<User, Guid> userRepository)
    {
        _userRepository = userRepository;
    }

    public void CreateUser(CreateUserInput input)
    {
        //Manually creating a User object from the CreateUserInput object
        var user = new User
        {
            Name = input.Name,
            Surname = input.Surname,
            EmailAddress = input.EmailAddress,
            Password = input.Password
        };

        _userRepository.Insert(user);
    }
}

CreateUserInput 和 User 是一個簡單的DTO實體類. 上面的代碼使用input對象創建了一個 User 實體. 上面的代碼很簡單,但在實際應用程序中 User 實體會擁有很多屬性,手動創建實體乏味且容易出錯. User 和 CreateUserInput 添加新屬性時還需要再去修改代碼.

我們需要一個庫自動處理類到類的映射. ABP提供了對象到對象映射的抽象並集成了AutoMapper做爲對象映射器。

IObjectMapper

IObjectMapper 接口 (在 Volo.Abp.ObjectMapping包中) 定義了一個簡單的 Map 方法. 上面的手動映射示例可以用以下方式重寫:

public class UserAppService : ApplicationService
{
    private readonly IRepository<User, Guid> _userRepository;

    public UserAppService(IRepository<User, Guid> userRepository)
    {
        _userRepository = userRepository;
    }

    public void CreateUser(CreateUserInput input)
    {
        //Automatically creating a new User object using the CreateUserInput object
        var user = ObjectMapper.Map<CreateUserInput, User>(input);

        _userRepository.Insert(user);
    }
}

示例中的 ObjectMapper 屬性在 ApplicationService 基類中屬性注入. 在其他地方也可以直接注入 IObjectMapper 接口。

Map方法有兩個泛型參數: 第一個是源對象類型,第二個是目標對象類型。

如果想要設置現有對象屬性,可以使用 Map 的重載方法:

public class UserAppService : ApplicationService
{
    private readonly IRepository<User, Guid> _userRepository;

    public UserAppService(IRepository<User, Guid> userRepository)
    {
        _userRepository = userRepository;
    }

    public async Task UpdateUserAsync(Guid id, UpdateUserInput input)
    {
        var user = await _userRepository.GetAsync(id);

        //Automatically set properties of the user object using the UpdateUserInput
        ObjectMapper.Map<UpdateUserInput, User>(input, user);

        await _userRepository.UpdateAsync(user);
    }
}

必須先定義映射,然後才能映射對象。請參閱AutoMapper集成部分了解如何定義映射。

AutoMapper 集成

AutoMapper 是最流行的對象到對象映射庫之一. Volo.Abp.AutoMapper程序包使用AutoMapper實現了 IObjectMapper.

定義了以下部分的映射後就可以使 IObjectMapper 接口。

定義映射

AutoMapper提供了多種定義類之間映射的方法. 有關詳細信息請參閱AutoMapper的文檔.

其中定義一種映射的方法是創建一個Profile 類. 例如:

public class MyProfile : Profile
{
    public MyProfile()
    {
        CreateMap<User, UserDto>();
    }
}

然後使用AbpAutoMapperOptions註冊配置文件:

[DependsOn(typeof(AbpAutoMapperModule))]
public class MyModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure<AbpAutoMapperOptions>(options =>
        {
            //Add all mappings defined in the assembly of the MyModule class
            options.AddMaps<MyModule>();
        });
    }
}

AddMaps 註冊給定類的程序集中所有的配置類,通常使用模塊類. 它還會註冊 attribute 映射

配置驗證

AddMaps 使用可選的 bool 參數控制模塊配置驗證:

options.AddMaps<MyModule>(validate: true);

如果此選項默認是 false , 但最佳實踐建議啓用。

可以使用 AddProfile 而不是 AddMaps 來控制每個配置文件類的配置驗證:

options.AddProfile<MyProfile>(validate: true);

如果你有多個配置文件,並且只需要爲其中幾個啓用驗證,那麼首先使用AddMaps而不進行驗證,然後爲你想要驗證的每個配置文件使用AddProfile.

高級主題

IObjectMapper 接口

假設你已經創建了一個可重用的模塊,其中定義了AutoMapper配置文件,並在需要映射對象時使用 IObjectMapper. 根據模塊化的性質,你的模塊可以用於不同的應用程序。

IObjectMapper 是一個抽象,可以由最終應用程序替換使用另一個映射庫. 這裏的問題是你的可重用模塊設計爲使用AutoMapper,因爲它爲其定義映射配置文件. 這種情況下即使最終應用程序使用另一個默認對象映射庫,你也要保證模塊始終使用AutoMapper。

IObjectMapper<TContext>將對象映射器上下文化,你可以爲不同的 模塊/上下文 使用不同的庫。

用法示例:

public class UserAppService : ApplicationService
{
    private readonly IRepository<User, Guid> _userRepository;   
    
    private readonly IObjectMapper<MyModule> _objectMapper;

    public UserAppService(
        IRepository<User, Guid> userRepository, 
        IObjectMapper<MyModule> objectMapper) //Inject module specific mapper
    {
        _userRepository = userRepository;
        _objectMapper = objectMapper;
    }

    public async Task CreateUserAsync(CreateUserInput input)
    {
        //Use the module specific mapper
        var user = _objectMapper.Map<CreateUserInput, User>(input);

        await _userRepository.InsertAsync(user);
    }
}

UserAppService 注入 IObjectMapper<MyModule>, 它是模塊的特定對象映射器,用法與 IObjectMapper 完全相同。

上面的示例代碼未使用 ApplicationService 中定義的 ObjectMapper 屬性,而是注入了 IObjectMapper<MyModule>. 但是 ApplicationService 定義了可以在類構造函數中設置的 ObjectMapperContext 屬性, 因此仍然可以使用基類屬性. 示例可以進行以下重寫:

public class UserAppService : ApplicationService
{
    private readonly IRepository<User, Guid> _userRepository;

    public UserAppService(IRepository<User, Guid> userRepository)
    {
        _userRepository = userRepository;
        //Set the object mapper context
        ObjectMapperContext = typeof(MyModule);
    }

    public async Task CreateUserAsync(CreateUserInput input)
    {
        var user = ObjectMapper.Map<CreateUserInput, User>(input);

        await _userRepository.InsertAsync(user);
    }
}

雖然使用上下文化的對象映射器與普通的對象映射器相同, 但是也應該在模塊的 ConfigureServices 方法中註冊上下文化的映射器:

[DependsOn(typeof(AbpAutoMapperModule))]
public class MyModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        //Use AutoMapper for MyModule
        context.Services.AddAutoMapperObjectMapper<MyModule>();

        Configure<AbpAutoMapperOptions>(options =>
        {
            options.AddMaps<MyModule>(validate: true);
        });
    }
}

IObjectMapper<MyModule>是可重用模塊的一項基本功能,可在多個應用程序中使用,每個模塊可以使用不同的庫進行對象到對象的映射. 所有預構建的ABP模塊都在使用它. 但是對於最終應用程序,你可以忽略此接口,始終使用默認的 IObjectMapper 接口:

IObjectMapper<TSource, TDestination> 接口

ABP允許自定義特定類的映射代碼. 假設你要創建一個自定義類從 User 映射到 UserDto. 這種情況下,你可以創建一個實現 IObjectMapper<User,UserDto>的類:

public class MyCustomUserMapper : IObjectMapper<User, UserDto>, ITransientDependency
{
    public UserDto Map(User source)
    {
        //TODO: Create a new UserDto
    }

    public UserDto Map(User source, UserDto destination)
    {
        //TODO: Set properties of an existing UserDto
        return destination;
    }
}

ABP會自動發現註冊 MyCustomUserMapper, 在你使用IObjectMapper將用戶映射到UserDto時會自動使用自定義映射。

一個類可以爲不同的對象實現多個 IObjectMapper<TSource,TDestination>。

這種方法功能強大, MyCustomUserMapper可以注入任何其他服務並在Map方法中使用。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章