对象到对象映射

    User 是一个简单的DTO和类. 上面的代码使用input对象创建了一个 User 实体. 上面的代码很简单,但在实际应用程序中 User 实体会拥有很多属性,手动创建实体乏味且容易出错. UserCreateUserInput 添加新属性时还需要再去修改代码.

    我们需要一个库自动处理类到类的映射. ABP提供了对象到对象映射的抽象并集成了AutoMapper做为对象映射器.

    IObjectMapper 接口 (在 包中) 定义了一个简单的 Map 方法. 上面的手动映射示例可以用以下方式重写:

    1. public class UserAppService : ApplicationService
    2. {
    3. private readonly IRepository<User, Guid> _userRepository;
    4. public UserAppService(IRepository<User, Guid> userRepository)
    5. {
    6. _userRepository = userRepository;
    7. }
    8. public void CreateUser(CreateUserInput input)
    9. {
    10. //Automatically creating a new User object using the CreateUserInput object
    11. var user = ObjectMapper.Map<CreateUserInput, User>(input);
    12. _userRepository.Insert(user);
    13. }
    14. }

    Map方法有两个泛型参数: 第一个是源对象类型,第二个是目标对象类型.

    如果想要设置现有对象属性,可以使用 Map 的重载方法:

    1. public class UserAppService : ApplicationService
    2. {
    3. private readonly IRepository<User, Guid> _userRepository;
    4. public UserAppService(IRepository<User, Guid> userRepository)
    5. {
    6. _userRepository = userRepository;
    7. }
    8. public async Task UpdateUserAsync(Guid id, UpdateUserInput input)
    9. {
    10. var user = await _userRepository.GetAsync(id);
    11. //Automatically set properties of the user object using the UpdateUserInput
    12. ObjectMapper.Map<UpdateUserInput, User>(input, user);
    13. await _userRepository.UpdateAsync(user);
    14. }
    15. }

    必须先定义映射,然后才能映射对象. 请参阅AutoMapper集成部分了解如何定义映射.

    AutoMapper 是最流行的对象到对象映射库之一. 程序包使用AutoMapper实现了 IObjectMapper.

    定义了以下部分的映射后就可以使 IObjectMapper 接口.

    AutoMapper提供了多种定义类之间映射的方法. 有关详细信息请参阅AutoMapper的文档.

    其中定义一种映射的方法是创建一个 类. 例如:

    1. public class MyProfile : Profile
    2. {
    3. public MyProfile()
    4. {
    5. CreateMap<User, UserDto>();
    6. }

    然后使用AbpAutoMapperOptions注册配置文件:

    1. [DependsOn(typeof(AbpAutoMapperModule))]
    2. public class MyModule : AbpModule
    3. {
    4. public override void ConfigureServices(ServiceConfigurationContext context)
    5. {
    6. Configure<AbpAutoMapperOptions>(options =>
    7. {
    8. //Add all mappings defined in the assembly of the MyModule class
    9. options.AddMaps<MyModule>();
    10. }
    11. }

    配置验证

    AddMaps 使用可选的 bool 参数控制的配置验证:

    如果此选项默认是 false , 但最佳实践建议启用.

    可以使用 AddProfile 而不是 AddMaps 来控制每个配置文件类的配置验证:

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

    允许为已存在的类定义额外属性. ABP 框架提供了一个映射定义扩展可以正确的映射两个对象的额外属性.

    1. public class MyProfile : Profile
    2. {
    3. public MyProfile()
    4. {
    5. CreateMap<User, UserDto>()
    6. .MapExtraProperties();
    7. }
    8. }

    如果两个类都是可扩展对象(实现了 IHasExtraProperties 接口),建议使用 MapExtraProperties 方法. 更多信息请参阅对象扩展文档.

    其他有用的扩展方法

    有一些扩展方法可以简化映射代码.

    忽视审计属性

    当你将一个对象映射到另一个对象时,通常会忽略审核属性.

    假设你需要将 ProductDto ()映射到Product实体,该实体是从 AuditedEntity 类继承的(该类提供了 CreationTime, CreatorId, IHasModificationTime 等属性).

    从DTO映射时你可能想忽略这些基本属性,可以使用 IgnoreAuditedObjectPropertie() 方法忽略所有审计属性(而不是手动逐个忽略它们):

    1. public class MyProfile : Profile
    2. {
    3. public MyProfile()
    4. {
    5. CreateMap<ProductDto, Product>()
    6. .IgnoreAuditedObjectProperties();
    7. }
    8. }

    还有更多扩展方法, 如 IgnoreFullAuditedObjectProperties()IgnoreCreationAuditedObjectProperties(),你可以根据实体类型使用.

    忽视其他属性

    1. public class MyProfile : Profile
    2. {
    3. public MyProfile()
    4. {
    5. CreateMap<SimpleClass1, SimpleClass2>()
    6. .ForMember(x => x.CreationTime, map => map.Ignore());
    7. }
    8. }

    我们发现它的长度是不必要的并且创建了 Ignore() 扩展方法:

    假设你已经创建了一个可重用的模块,其中定义了AutoMapper配置文件,并在需要映射对象时使用 IObjectMapper. 根据模块化的性质,你的模块可以用于不同的应用程序.

    IObjectMapper 是一个抽象,可以由最终应用程序替换使用另一个映射库. 这里的问题是你的可重用模块设计为使用AutoMapper,因为它为其定义映射配置文件. 这种情况下即使最终应用程序使用另一个默认对象映射库,你也要保证模块始终使用AutoMapper.

    IObjectMapper<TContext>将对象映射器上下文化,你可以为不同的 模块/上下文 使用不同的库.

    用法示例:

    1. public class UserAppService : ApplicationService
    2. {
    3. private readonly IRepository<User, Guid> _userRepository;
    4. private readonly IObjectMapper<MyModule> _objectMapper;
    5. public UserAppService(
    6. IRepository<User, Guid> userRepository,
    7. IObjectMapper<MyModule> objectMapper) //Inject module specific mapper
    8. {
    9. _userRepository = userRepository;
    10. _objectMapper = objectMapper;
    11. }
    12. {
    13. //Use the module specific mapper
    14. await _userRepository.InsertAsync(user);
    15. }
    16. }

    UserAppService 注入 IObjectMapper<MyModule>, 它是模块的特定对象映射器,用法与 IObjectMapper 完全相同.

    上面的示例代码未使用 ApplicationService 中定义的 ObjectMapper 属性,而是注入了 IObjectMapper<MyModule>. 但是 ApplicationService 定义了可以在类构造函数中设置的 ObjectMapperContext 属性, 因此仍然可以使用基类属性. 示例可以进行以下重写:

    1. public class UserAppService : ApplicationService
    2. {
    3. private readonly IRepository<User, Guid> _userRepository;
    4. public UserAppService(IRepository<User, Guid> userRepository)
    5. {
    6. _userRepository = userRepository;
    7. //Set the object mapper context
    8. ObjectMapperContext = typeof(MyModule);
    9. }
    10. public async Task CreateUserAsync(CreateUserInput input)
    11. {
    12. var user = ObjectMapper.Map<CreateUserInput, User>(input);
    13. await _userRepository.InsertAsync(user);
    14. }
    15. }

    虽然使用上下文化的对象映射器与普通的对象映射器相同, 但是也应该在模块的 ConfigureServices 方法中注册上下文化的映射器:

    1. [DependsOn(typeof(AbpAutoMapperModule))]
    2. public class MyModule : AbpModule
    3. {
    4. public override void ConfigureServices(ServiceConfigurationContext context)
    5. {
    6. //Use AutoMapper for MyModule
    7. context.Services.AddAutoMapperObjectMapper<MyModule>();
    8. Configure<AbpAutoMapperOptions>(options =>
    9. {
    10. options.AddMaps<MyModule>(validate: true);
    11. });
    12. }
    13. }

    IObjectMapper<MyModule>是可重用模块的一项基本功能,可在多个应用程序中使用,每个模块可以使用不同的库进行对象到对象的映射. 所有预构建的ABP模块都在使用它. 但是对于最终应用程序,你可以忽略此接口,始终使用默认的 IObjectMapper 接口.

    IObjectMapper<TSource, TDestination> 接口

    ABP允许自定义特定类的映射代码. 假设你要创建一个自定义类从 User 映射到 UserDto. 这种情况下,你可以创建一个实现 IObjectMapper<User,UserDto>的类 :

    1. public class MyCustomUserMapper : IObjectMapper<User, UserDto>, ITransientDependency
    2. {
    3. public UserDto Map(User source)
    4. {
    5. //TODO: Create a new UserDto
    6. }
    7. public UserDto Map(User source, UserDto destination)
    8. {
    9. //TODO: Set properties of an existing UserDto
    10. return destination;
    11. }
    12. }

    ABP会自动发现注册 MyCustomUserMapper, 在你使用IObjectMapper将用户映射到UserDto时会自动使用自定义映射.

    一个类可以为不同的对象实现多个 IObjectMapper<TSource,TDestination>.