浅入 ABP 系列(7):对象映射
写博客的过程中,发现很多基础理论太薄弱,因此很多专业词汇可能会解释错误或者不准确,建议读者多参考官方文档或者其它书籍。
本篇主要讲解 ABP 中如何配置、使用对象映射,其中大部分跟 AutoMapper 这个框架有关,建议读者预先学习这个框架,可参考笔者的另一篇博客:浅入 AutoMapper
基础
DTO和实体
实体
实体是领域驱动设计(Domain Driven Design)中的概念,实体通常一一映射某些对象的固有属性,最常使用的是关系型数据库中的表。
在 ABP 中,实体位于领域层中,实体类需要实现 IEntity<TKey> 接口或继承 Entity<TKey> 基类,示例如下:
public class Book : Entity<Guid>
{
public string Name { get; set; }
public float Price { get; set; }
}
DTO
数据传输对象(Data Transfer Object),作为数据传输过程中的数据模型,用于在应用层和表示层之间传输数据。
在 ABP 中,DTO 位于应用服务层,即本系列文章示例源码中的 AbpBase.Application 项目。
通常表示层或其它类型的客户端调用应用服务时,将 DTO 作为参数传递,它使用领域对象(实体)执行某些特定的业务逻辑,并将 DTO (跟传入的 DTO 不是同一个)返回到表示层中,因此表示层与领域层完全隔离。
DTO 类 可能会跟 实体类的字段/属性高度相似,为每个服务的每个方法创建 DTO 类可能会很枯燥且费时间。
ABP 的 DTO 类示例如下:
public class ProductDto : EntityDto<Guid>
{
public string Name { get; set; }
//...
}
麻烦的映射
前面提到,领域层和应用服务层是要隔离的,例如以下伪代码:
class HomeController
{
AddService _service;
[HttpPost]
public int AddEquip(EquipDto dto)
{
return _service.Add(dto).Id;
}
}
class AddService
{
DataContext _context;
EquipDto Add(EquipDto dto)
{
Equip equip = new Equip()
{
Name = dto.Name;
};
_context.Equip.Add(equip);
_context.SaveChange();
dto.Id = equip.Id;
return dto;
}
}
class EquipDto
{
int Id;
string Name;
}
----------
class Equip
{
int Id;
string Name;
}
这样每次都需要手动为 DTO 类和 实体类手动对字段赋值映射,当一个实体有数十个字段时,写出的代码会很冗长,而且容易忽略了某些字段,最终导致了 Bug。
大家都知道, AutoMapper 正好可以解决这个问题。
AutoMapper 集成
ABP 的 Volo.Abp.AutoMapper 模块封装或集成了 AutoMapper,所以我们正好使用模块,为 ABP 应用定义对象映射。
关于 AutoMapper 的使用,如何配置 Profile 等,笔者已经单独写到 浅入 AutoMapper,请点击链接另外学习 AutoMapper 的使用。
我们可以在 AbpBase.Application 项目中,新建 一个 AbpBaseApplicationAutoMapperProfile.cs 文件,这个文件用于实现 Profile 以及定义映射。将服务领域的映射集中到这个文件中;或者新建一个 Profiles 文件夹,在其中存放一些 Profile 类。
其内容如下:
public class AbpBaseApplicationAutoMapperProfile:Profile
{
public AbpBaseApplicationAutoMapperProfile()
{
//base.CreateMap<MyEntity,MyDto>();
}
}
定义完毕后,需要配置 AutoMapper 依赖注入,可在 AbpBaseApplicationModule 的 ConfigureServices 方法中,增加以下代码:
Configure<AbpAutoMapperOptions>(options =>
{
// 以模块为单位注册映射
options.AddMaps<AbpBaseApplicationModule>();
//// 以单个 Profiel 为单位注册映射
//options.AddProfile<AbpBaseApplicationAutoMapperProfile>();
});
在 Debug 阶段,我们担心项目改动代码时,新增的字段忘记了加入到映射配置中,或者其它情况,在 AutoMapper 中,我们可以使用 configuration.AssertConfigurationIsValid(); 来检查映射;在 ABP 中则可使用 validate: true 参数来开启检查。
Configure<AbpAutoMapperOptions>(options =>
{
// 以模块为单位注册映射
options.AddMaps<AbpBaseApplicationModule>(validate: true);
//// 以单个 Profiel 为单位注册映射
//options.AddProfile<AbpBaseApplicationAutoMapperProfile>(validate: true);
});
IObjectMapper/ObjectMapper
在 AbpBase.Application 项目中,添加 Nuget 包,搜索 Volo.Abp.ObjectMapping 并下载相应的稳定版本。
IObjectMapper 有两个,一个是 AutoMapper 的接口,一个是 Volo.Abp.ObjectMapping 的 泛型接口。
AutoMapper 的 IObjectMapper 不好用,所以别用;用 Volo.Abp.ObjectMapping 的 IObjectMapper <接口>。
ObjectMapper 是 AutoMapper 中的,我们可以直接在控制器等位置,使用 ObjectMapper 注入,然后通过 ObjectMapper 实例映射对象。
ObjectMapper 只有 .Map() 这个方法用得顺手。
private readonly ObjectMapper<T1,T2> _mapper;
public TestController(ObjectMapper<T1,T2> mapper)
{
_mapper = mapper;
// ... 使用示例
_ = mapper.Map<T1> ();
}
也可以通过依赖注入使用 IObjectMapper 接口。
但是因为 ObjectMapper 是泛型类,每种类型的 DTO 都要注入一次的话,会很麻烦,因此这种方案也可以抛弃。
而 泛型的 IObjectMapper<TModule> 是一个抽象,我们使用 IObjectMapper<TModule> 做依赖注入的话,后续如果替换为别的对象映射框架,则不需要修改原有代码即可完成替代。而且 IObjectMapper<TModule> 比较舒服。
使用示例:
private readonly IObjectMapper<AbpBaseApplicationModule> _mapper;
public TestController(IObjectMapper<AbpBaseApplicationModule> mapper)
{
_mapper = mapper;
// ... 使用示例
_ = mapper.Map<...>();
}
对象拓展
ABP框架提供了 实体扩展系统 允许你 添加额外属性 到已存在的对象 无需修改相关类。这句话是抄 ABP 官方文档的。
要支持对象拓展映射,则需要开启配置:
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<User, UserDto>()
.MapExtraProperties();
}
}
时间有限,笔者这里只把官方文档的内容讲清楚,读者看完后,需要继续查阅官方文档,完整了解对象拓展。
ObjectExtensionManager 是一个拓展对象映射类,可以显式为类拓展一些额外的属性,这个类型在 Volo.Abp.ObjectMapping 中定义。
ObjectExtensionManager 是一个类型,但是我们不能直接 new 它,或者使用依赖注入,只能通过 ObjectExtensionManager.Instance 这个属性获取新的类型。我们无需关心它是用了啥设计模式,还是因为缓存之类的原因这样设计。
ObjectExtensionManager 有两种属性,其说明如下:
AddOrUpdate:是定义对象额外属性或更新对象额外属性的主要方法;AddOrUpdateProperty:快捷地定义单个拓展属性的方法;
AddOrUpdateProperty 用于定义单个属性,AddOrUpdate 是一个容器,可以包含多个 AddOrUpdateProperty 。
AddOrUpdateProperty 示例代码如下:
ObjectExtensionManager.Instance
.AddOrUpdateProperty<TestA, string>("Name");
// 为 TestA 类添加了一个 G 属性
AddOrUpdate 的示例代码如下:
ObjectExtensionManager.Instance
.AddOrUpdate<TestA>(options =>
{
options.AddOrUpdateProperty<string>("Name");
options.AddOrUpdateProperty<bool>("Nice");
}
);
当然,我们还可以同时为多个类型同时定义一个额外的属性:
ObjectExtensionManager.Instance
.AddOrUpdateProperty<string>(
new[]
{
typeof(TestA),
typeof(TestB),
typeof(TestC)
},
"Name"
);
如果需要定义多个属性,则可以使用 AddOrUpdate:
ObjectExtensionManager.Instance
.AddOrUpdate(options =>
{
options.AddOrUpdateProperty<string>("Name");
}, new[]{
typeof(TestA),
typeof(TestB)
});
另外它还可以设置默认值、增加验证规则等,这些笔者就不再赘述,读者感兴趣可以点击链接进入官方文档查看。
https://docs.abp.io/zh-Hans/abp/latest/Object-Extensions#validation
浅入 ABP 系列(7):对象映射的更多相关文章
- 浅入 ABP 系列(4):事件总线
浅入 ABP 系列(4):事件总线 版权护体作者:痴者工良,微信公众号转载文章需要 <NCC开源社区>同意. 目录 浅入 ABP 系列(4):事件总线 事件总线 关于事件总线 为什么需要这 ...
- 浅入ABP(1):搭建基础结构的 ABP 解决方案
浅入ABP(1):搭建基础结构的 ABP 解决方案 目录 浅入ABP(1):搭建基础结构的 ABP 解决方案 搭建项目基础结构 ApbBase.Domain.Shared 创建过程 ApbBase.D ...
- 浅入 AutoMapper
目录 浅入 AutoMapper AutoMapper 基本使用 映射配置 映射检查 性能 Profile 配置 依赖注入 表达式与 DTO 浅入 AutoMapper 在 Nuget 搜索即可安装, ...
- ABP(现代ASP.NET样板开发框架)系列之16、ABP应用层——数据传输对象(DTOs)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之16.ABP应用层——数据传输对象(DTOs) ABP是“ASP.NET Boilerplate Project ...
- Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例
在Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们看到了XMLConfigBuilder(xml配置解析器)的实例化.而且这个实例化过程在文章:Mybatis源码解析,一步一步从浅 ...
- Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取
在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 用AutoMapper搞定对象映射
上一篇文章(https://www.cnblogs.com/meowv/p/12961014.html)集成了定时任务处理框架Hangfire,完成了一个简单的定时任务处理解决方案. 本篇紧接着来玩一 ...
- 浅入深出Vue系列
浅入深出Vue导航 导航帖,直接点击标题即可. 文中所有涉及到的资源链接均在最下方列举出来了. 前言 基础篇 浅入深出Vue:工具准备之WebStorm搭建及配置 浅入深出Vue之工具准备(二):Po ...
- MyBitis(iBitis)系列随笔之二:类型别名(typeAliases)与表-对象映射(ORM)
类型别名(typeAliases): 作用:通过一个简单的别名来表示一个冗长的类型,这样可以降低复杂度. 类型别名标签typeAliases中可以包含多个typeAlias,如下 < ...
- Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码
在文章:Mybatis源码解析,一步一步从浅入深(一):创建准备工程,中我们为了解析mybatis源码创建了一个mybatis的简单工程(源码已上传github,链接在文章末尾),并实现了一个查询功能 ...
随机推荐
- Windows 可以操纵linux内文件,与本地一致的工具
https://github.com/allanrbo/filesremote/releases/ 感觉挺好的.
- fiddler如何抓取https请求
pc端browse 1.打开下载好的fiddler,点击tools选择options后进入https tab下,勾选Decrypt HTTPS CONNECTS 和Ignore server cer ...
- 大数据面试题集锦-Hadoop面试题(二)-HDFS
你准备好面试了吗?这里有一些面试中可能会问到的问题以及相对应的答案.如果你需要更多的面试经验和面试题,关注一下"张飞的猪大数据分享"吧,公众号会不定时的分享相关的知识和资料. 目录 ...
- P7900 [COCI2006-2007#2] SJECIŠTA_题解
[COCI2006-2007#2] SJECIŠTA_题解 rt 我们来看一下题目描述 考虑一个有 \(n\) 个顶点的凸多边形,且这个多边形没有任何三个(或以上) 的对角线交于一点. 这句话什么意思 ...
- ABP 使用Except 和EqualityHelper<T> 实现去重
先上一端代码!!! railwayCar中有10条记录,train参考railwayCar创建了5条记录.要实现,当train再次参考railwayCar创建记录时,使用过的记录在展示列表时不可以再次 ...
- vim 从嫌弃到依赖(23)——最后的闲扯
截止到上一篇文章,关于vim的基础操作都已经讨论完了,这篇我主要就是闲扯,瞎聊.就想毕业论文都有一个致谢一样,这篇我们就作为整个系列的致谢吧 学习vim到底能给我们带来什么 学习vim到底能给我们带来 ...
- python处理Excel实现自动化办公教学(数据筛选、公式操作、单元格拆分合并、冻结窗口、图表绘制等)【三】
相关文章: python处理Excel实现自动化办公教学(含实战)[一] python处理Excel实现自动化办公教学(含实战)[二] python处理Excel实现自动化办公教学(数据筛选.公式操作 ...
- 一个关于用netty的小错误反思
一个关于用netty的小认知 在使用netty时,观看了黑马的netty网课,没想就直接用他的依赖了 依赖如下 <dependency> <groupId>io.netty&l ...
- 23.2 SEH之异常处理--《Windows核心编程》结构化异常处理
(structured exception handing)SEH 包含终止处理和异常处理.本章讨论异常处理. 当一个硬件或者软件异常被抛出的时候,操作系统会给我们程序一个查看异常类型的机会,并允 ...
- .NET Core开发实战(第26课:工程结构概览:定义应用分层及依赖关系)--学习笔记
26 | 工程结构概览:定义应用分层及依赖关系 从这一节开始进入微服务实战部分 这一节主要讲解工程的结构和应用的分层 在应用的分层这里定义了四个层次: 1.领域模型层 2.基础设施层 3.应用层 4. ...