对象到对象的映射

介绍

  映射一个相似的对象到另一个对象是常有的事情。因为两个对象(类)有相似/相同的属性,所以两个相似对象之间相互映射是乏味、重复的。考虑一个经典的应用服务方法如下:

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是一个简单的DTO,User是一个简单的实体。我们根据给定的input手动创建了一个User实体。在实际的应用中,用户实体会有许多的属性,手动创建它变得乏味且容易出错的。当我们想给User和CreateUserInput添加新属性时,需要改变映射代码。

  我们可以使用一个类库来自动映射。AutoMapper是对象到对象映射最好的类库之一。ABP定义了IObjectMapper接口进行抽象,并在Abp.AutoMapper包里使用AutoMapper实现了这个接口。

IObjectMapper接口

  IObjectMapper是一个简单的抽象,它包含Map方法用来映射一个对象到另一个。我们可以把上面的实例代码修改如下:

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);
}

AutoMapper集成

  Abp.AutoMapper nuget包(模块)实现了IObjectMapper且提供了额外的特征。

安装

  首先,在工程中安装Abp.AutoMapper包:

Install-Package Abp.AutoMapper

  然后,添加AbpAutoMapperModule依赖到模块定义类中:

[DependsOn(typeof(AbpAutoMapperModule))]
public class MyModule : AbpModule
{
...
}

  然后就可以在代码里安全的注入和使用IObjectMapper。当需要的时候,也可以使用AutoMapper自己的API。

创建映射

  AutoMapper需要定义一个类之间的映射在使用它之前。可以参见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,用户用Email属性来标示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模块使用本地映射实例(使用单例模式注册到依赖注入)而不是静态实例(Abp.AutoMapper默认使用Mapper.Instance实例,这样就允许使用MapTo扩展方法)。

Configuration.Modules.AbpAutoMapper().UseStaticMapper = false;

预定义映射

LocalizableString->string

  Abp.AutoMapper模块定义了一个映射,用来转换本地字符串(或者ILocalizableString)对象到string对象。它使用ILocalizationManager进行转换,所以localizable属性在任何类的映射过程中会自动本地化。

注入IMapper

  可能需要直接使用AutoMapper的IMapper对象而不是IObjectMapper抽象。在这种情况下,在类中注入并使用IMapper。Abp.AutoMapper包以单例形式将IMapper注册到依赖注入。

返回主目录

ABP官方文档翻译 2.7 对象到对象的映射的更多相关文章

  1. ABP官方文档翻译 4.3 校验数据传输对象

    校验数据传输对象 校验简介 使用数据标注 自定义校验 禁用校验 标准化 校验简介 应用的输入首先应该被校验.输入可以是用户的也可以是其他应用的.在一个web应用中,校验通常实现两次:客户端和服务端.客 ...

  2. 0.0 ABP官方文档翻译目录

    一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...

  3. ABP官方文档翻译 6.4 导航

    导航 创建菜单 注册导航提供者 显示菜单 每一个网络应用都会有一些菜单用来在pages/screens之间导航.ABP提供了通用的基础设施来创建并显示菜单. 创建菜单 应用可以由不同的模块组成,每一个 ...

  4. ABP官方文档翻译 2.2 ABP会话

    ABP会话 介绍 关于IAbpSession 注入会话 会话属性 覆盖当前会话值 警告! 用户标示 介绍 如果应用需要登录的话,同样也需要知道当前用户可以执行哪些操作.ABP在展现层提供了会话对象,同 ...

  5. ABP官方文档翻译 0.0 ABP官方文档翻译目录

    一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...

  6. ABP官方文档翻译 4.2 数据传输对象

    数据传输对象 DTOs的必要性 领域层的抽象 数据隐藏 序列化和懒加载问题 DTO转换和验证 示例 DTOs和实体间的自动映射 辅助接口和类 数据传输对象用来在应用层和展示层之间传输数据. 展示层调用 ...

  7. ABP官方文档翻译 3.2 值对象

    值对象 介绍 值对象基类 最佳实践 介绍 "展现领域描述性层面且没有概念性身份的对象称之为值对象."(Eric Evans). 和实体相反,实体有身份标示(Id),值对象没有身份标 ...

  8. ABP官方文档翻译 10.1 ABP Nuget包

    ABP Nuget包 Packages Abp Abp.AspNetCore Abp.Web.Common Abp.Web Abp.Web.Mvc Abp.Web.Api Abp.Web.Api.OD ...

  9. ABP官方文档翻译 2.5 设置管理

    设置管理 介绍 关于 ISettingStore 定义设置 设置范围 重写设置定义 获取设置值 服务端 客户端 更改设置 关于缓存 介绍 每个应用都需要存储设置,并且在应用的某些地方需要使用这些设置. ...

随机推荐

  1. CTF---编程入门第一题 循环

    循环分值:10 来源: 北邮天枢战队 难度:易 参与人数:1478人 Get Flag:467人 答题人数:523人 解题通过率:89% 给出一个循环公式,对于一个整数n,当n为奇数时,n=3n+1, ...

  2. 51Nod 1090 3个数和为0(暴力)

    1090 3个数和为0 基准时间限制:1 秒 空间限制:131072 KB 分值: 5         难度:1级算法题 给出一个长度为N的无序数组,数组中的元素为整数,有正有负包括0,并互不相等.从 ...

  3. HDU 2639 Bone Collector II(01背包变形【第K大最优解】)

    Bone Collector II Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  4. BZOJ3997: [TJOI2015]组合数学(网络流)

    3997: [TJOI2015]组合数学 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 405  Solved: 284[Submit][Status ...

  5. 三维dp&codeforce 369_2_C

    三维dp&codeforce 369_2_C 标签: dp codeforce 369_2_C 题意: 一排树,初始的时候有的有颜色,有的没有颜色,现在给没有颜色的树染色,给出n课树,用m种燃 ...

  6. android企业级商城源码、360°全景图VR源码、全民直播源码等

    Android精选源码 [新版]Android技术博客精华汇总 开源了:乐乐音乐5.0-Android音乐播放器 android实现仿真水波纹效果源码 360°全景图VR,这是一个值得把玩的APP a ...

  7. 前端自动化-----gulp详细入门(转)

    简介: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成:使用她,我们不仅可以很愉快的编写代码 ...

  8. Python3 的分支与循环

    1:条件分支 if 条件 : 语句 else: 语句 2.缩写 else: if : 可以简写为 elif ,因此Python 可以有效的避免"悬挂else" 举例: #悬挂els ...

  9. JavaScript URL传值过程中遇到的问题及知识点总结

    JavaScript URL传值过程中遇到的问题及知识点总结 Web系统开发过程中经常用到URL进行传值,刚刚接触时不太会解析,会出现中文乱码问题等. 1.父子页面之间的传值(在一个页面中以加载ifr ...

  10. enum 操作

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...