ABP文档 - 对象与对象之间的映射
本节内容:
把一个对象映射到另一个相似的对象很常见,两个对象(类)具有相似或相同的属性,它们之间要互相映射,其实这项工作重复且无聊,考虑一个典型的应用服务方法,如下:
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作为单例注册到了依赖注入系统里。
kid1412声明:转载请把此段声明完整地置于文章页面明显处,并保留个人在博客园的链接:http://www.cnblogs.com/kid1412/(可点击跳转)。