对象到对象的映射

介绍

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

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. 最短路 spfa 算法 && 链式前向星存图

    推荐博客  https://i.cnblogs.com/EditPosts.aspx?opt=1 http://blog.csdn.net/mcdonnell_douglas/article/deta ...

  2. linux 操作系统/xxx目录下都是什么文件?

    /bin:存放最常用命令: /dev:设备文件: /etc:存放各种配置文件: /home:用户主目录: /lib:系统最基本的动态链接共享库: /mnt:一般是空的,用来临时挂载别的文件系统: /b ...

  3. Centos7安装docker-compse踩过的坑

    一.概要 ​ 本文,我们介绍如何在centos7环境下安装docker-compose, 记录下安装过程步骤以及遇到的问题还有解决办法. 二.安装方式 1.官方安装方式 sudo curl -L ht ...

  4. 如何开发由Create-React-App 引导的应用(三)

    此文章是翻译How to develop apps bootstrapped with Create React App 官方文档 系列文章 如何开发由Create-React-App 引导的应用 如 ...

  5. sql 中文转拼音首字母

    http://blog.csdn.net/zhanglong_longlong/article/details/46772571 --可支持大字符集20000个汉字! create function ...

  6. Android调试错误-No resource identifier found for attribute 'showAsAction'

    转载自:http://www.bubuko.com/infodetail-498830.html 1.问题描述: 24\YoumiAndroidSdk\demo\offers\res\menu\mai ...

  7. MySQL字符集设置—MySQL数据库乱码问题

    MySQL(4.1以后版本) 服务器中有六个关键位置使用了字符集的概念,他们是:client .connection.database.results.server .system.MySQL有两个字 ...

  8. [Qt Quick] No rule to make target问题解决办法

    [问题描述] 修改项目中资源的qml文件名或删除无用资源文件后,重新构建项目时,会出现类似如下的问题提示: No rule to make target 'aaa', needed by 'bbb'. ...

  9. BSA Network Shell系列-nsh命令

    nsh nsh命令软链接到zsh,直接运行nsh可进入Network Shell,所有的Network Shell命令都需要运行nsh进入Network Shell执行 1 使用cd命令访问远程主机和 ...

  10. 在Tomcat中配置单点登录

    单点登录:Single Sign-On .概述 一旦你设置了realm和验证的方法,你就需要进行实际的用户登录处理.一般说来,对用户而言登录系统是一件很麻烦的事情,你必须尽量减少用户登录验证的次数.作 ...