对象到对象的映射

介绍

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

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. 【Java学习笔记之二】java标识符命名规范

    什么是标识符 就是程序员在定义java程序时,自定义的一些名字.标识符可以应用在类名.变量.函数名.包名上. 标识符必须遵循以下规则 标识符由26个英文字符大小写(a~zA~Z).数字(0~9).下划 ...

  2. [bzoj1242] Zju1015 Fishing Net弦图判定

    弦图判定..MCS算法. 先选一个点,然后每次拿 相邻已选点最多 的未选点. 选完之后判断一下是否是完美消除序列. #include<cstdio> #include<iostrea ...

  3. Codeforces Round #328 (Div. 2)_A. PawnChess

    A. PawnChess time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  4. [数据库] SQL查询语句表行列转换及一行数据转换成两列

    原文来自:http://blog.csdn.net/Eastmount/article/details/50559008 本文主要讲述了SQL查询语句表之间的行列转换,同时也包括如何将一行数据转换成两 ...

  5. mysql习题

    如图表创建数据库. create table class( cid int auto_increment primary key, caption ) )engine=innodb default c ...

  6. Nginx负载均衡配置简单配置方法

    http://www.jb51.net/article/121235.htm Nginx作为负载均衡服务器,用户请求先到达nginx,再由nginx根据负载配置将请求转发至不同的Web服务器.下面通过 ...

  7. curl说明

    https://baike.baidu.com/item/curl/10098606?fr=aladdin curl是利用URL语法在命令行方式下工作的开源文件传输工具.它被广泛应用在Unix.多种L ...

  8. php对数组进行分页

      3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ...

  9. JS_全

    <script src="jquery-1.9.1.js" type="text/javascript"></script> <s ...

  10. 【开发技术】java中代码检查checkStyle结果分析

    编写Javadoc代码在Java代码的类.函数.数据成员前中输入/**回车,Eclipse能够自动生成相应的Javadoc代码.可以在后面添加相关的文字说明. Type is missing a ja ...