ABP官方文档翻译 4.2 数据传输对象
数据传输对象
数据传输对象用来在应用层和展示层之间传输数据。
展示层调用应用服务方法并传递一个数据传输对象(DTO),然后应用服务使用领域对象执行一些特定的业务逻辑并返回给展示层一个DTO。因此,展示层完全与领域层隔离。在一个理想的分层应用中,展示层不直接使用领域对象(仓储、实体...)。
DTOs的必要性
最初,为每一个应用服务方法创建DTO看起来乏味且耗时,但是如果你正确使用它,他们会拯救你的应用。为什么这么说呢?
领域层的抽象
DTOs提供了一种从展示层抽象领域对象的有效方式。因此,你的分层是正确分离的。即使你想完全改变展示层,你可以继续使用已有的应用和领域层。相反,你可以重写领域层,完全改变数据模式、实体和ORM框架而展示层不用做任何改变,只要你应用服务的约定(方法签名和DTOs)保持不变。
数据隐藏
假如你有一个user实体,它有属性Id,Name,EmailAddress和Password。如果UserAppService的GetAllUsers()方法返回一个List<User>,任何人都可以看到所有用户的密码,即使你不在屏幕上显示。这不是关于安全,而是数据隐藏。应用服务应该给展示层返回它需要的。不是更多而是更少。
序列化和懒加载问题
当你返回一个数据(对象)到展示层时,它可能会在某处被序列化。例如,在一个返回JSON的MVC方法里,你的对象被序列化为JSON并发送到客户端。在这种情况下,返回实体到展示层是有问题的。如何?
在一个真实应用里,你的实体将会互相引用。User实体引用Roles。所以,如果你想序列化User,它的Role是也被序列化。甚至Role类或许有一个List<Permission> ,Permission类可能引用PermissionGroup类等等...你能想象所有这些对象都被序列化吗?你可能意外的序列化了整个数据库。如果你的对象存在循环引用,它就不能被序列化。
解决方案是什么呢?标记属性为不可序列化?不,你不知道什么时候它被序列化什么时候不被序列化。它可能在一个应用服务方法里需要序列化,在其他却不需要。所以,在这种情况下返回一个安全的可序列化的、特定设计的DTOs是一个好的选择。
几乎所有的ORM框架支持懒加载。当需要的时候从数据库加载实体。比如说User类有一个Role类的引用。当你从数据库里获取一个User时,Role属性没有填充。当你第一次读Role属性时,它才从数据库加载。所以,如果你返回给展示层一个实体,它将导致从数据库中提取额外的实体。如果一个序列化工具读取这个实体,它递归读取所有属性,你的整个数据库再一次被提取了(如果实体之间有恰当的关系的话)。
我们可以说更多关于在展示层使用实体的问题。最好不要引用包含领域(业务)层到展示层的程序集。
DTO转换和验证
ABP强支持DTOs。它提供了关于DTOs的一些约定类和接口并建议了一些命名和使用约定。当你编写如这里描述的代码时,ABP简单的自动处理一些任务。
示例
让我们看一个完整的示例。比如说我们想开发一个应用服务方法,用来查询people,提供一个name参数并返回people列表。在这种情况下,我们有一个Person实体,如下所示:
public class Person : Entity
{
public virtual string Name { get; set; }
public virtual string EmailAddress { get; set; }
public virtual string Password { get; set; }
}
我们可以为我们的应用服务定义一个接口:
public interface IPersonAppService : IApplicationService
{
SearchPeopleOutput SearchPeople(SearchPeopleInput input);
}
ABP建议以MethodNameInput和MethodNameOutput的形式命名input/output参数,并为每个应用服务方法定义一个分离的input和outputDTO。即使你的方法只接收或返回衣蛾参数,最好也创建一个DTO类。因此,你的代码将更易扩展。你可以以后添加更多的属性而不用更改你的方法的签名并且不会破坏已有的客户端应用。
当然,如果没有返回值的话,你的方法可以返回void。如果你以后添加一个返回值,它不会破坏已有的应用。如果你的方法不接受任何参数,你就不需要定义一个input DTO。但是,如果将来有可能添加参数,最好还是写一个input DTO类。由你来决定。
让我们来看看这个示例定义的input和output DTO类:
public class SearchPeopleInput
{
[StringLength(, MinimumLength = )]
public string SearchedName { get; set; }
} public class SearchPeopleOutput
{
public List<PersonDto> People { get; set; }
} public class PersonDto : EntityDto
{
public string Name { get; set; }
public string EmailAddress { get; set; }
}
ABP在方法执行前自动校验input。和ASP.NET MVC的验证类似,但是注意应用服务不是一个控制器,它是一个普通的C#类。ABP自动拦截并检查input。这有更多关于验证的,参见DTO validation文档。
EntityDto是一个简单的类,它声明了Id属性,因为它对所有的实体都通用。如果你的实体主键不是int,有一个泛型版本。你不用必须使用它,但是建议定义一个Id属性。
PersonDto如你所见不包含Password属性,因为对展示层来说是不需要的。甚至发送所有people的密码到展示层是危险的。想想一个JavaScript客户端请求它,任何人都很容易的抓取到所有的密码。
在进行下一步之前我们先实现IPersonAppService:
public class PersonAppService : IPersonAppService
{
private readonly IPersonRepository _personRepository; public PersonAppService(IPersonRepository personRepository)
{
_personRepository = personRepository;
} public SearchPeopleOutput SearchPeople(SearchPeopleInput input)
{
//Get entities
var peopleEntityList = _personRepository.GetAllList(person => person.Name.Contains(input.SearchedName)); //Convert to DTOs
var peopleDtoList = peopleEntityList
.Select(person => new PersonDto
{
Id = person.Id,
Name = person.Name,
EmailAddress = person.EmailAddress
}).ToList(); return new SearchPeopleOutput { People = peopleDtoList };
}
}
我们从数据库获取实体,把他们转换成DTOs并返回output。注意,我们不校验input,ABP校验它。它甚至检查input参数是否为null,如果是便抛出异常。这使得我们不用再每个方法里编写检查语句。
但是,可能你不喜欢从一个Person实体到一个PersonDto对象的转换代码。它确实是一个乏味的工作。Person实体有更多的属性。
DTOs和实体间的自动映射
幸运的是有工具可以使得这个非常简单。AutoMapper就是其中之一。参见AutoMapper集成文档了解如何使用它。
辅助接口和类
ABP提供了一些帮助接口,可以用来实现以标准化通用DTO的属性名称。
ILimitedResultRequest定义了MaxResultCount属性。所以,你可以在你的input DTOs里实现它来标准化限制结果集。
IPageResultRequest通过添加SkipCount扩展了ILimitedResultRequest。所以,我们可以在SearchPeopleInput分页里实现这个接口:
public class SearchPeopleInput : IPagedResultRequest
{
[StringLength(, MinimumLength = )]
public string SearchedName { get; set; } public int MaxResultCount { get; set; }
public int SkipCount { get; set; }
}
作为一个分页请求的结果,你可以返回一个实现IHasTotalCount接口的output DTO。命名标准化帮助我们创建可复用的代码和约定。参见Abp.Application.Services.Dto命名空间下的其他接口和类。
ABP官方文档翻译 4.2 数据传输对象的更多相关文章
- ABP官方文档翻译 3.2 值对象
值对象 介绍 值对象基类 最佳实践 介绍 "展现领域描述性层面且没有概念性身份的对象称之为值对象."(Eric Evans). 和实体相反,实体有身份标示(Id),值对象没有身份标 ...
- ABP官方文档翻译 4.3 校验数据传输对象
校验数据传输对象 校验简介 使用数据标注 自定义校验 禁用校验 标准化 校验简介 应用的输入首先应该被校验.输入可以是用户的也可以是其他应用的.在一个web应用中,校验通常实现两次:客户端和服务端.客 ...
- ABP官方文档翻译 4.1 应用服务
应用服务 IApplicationService接口 ApplicationService类 CrudService和AsyncCrudAppService类 简单的CRUD应用服务示例 自定义CRU ...
- ABP官方文档翻译 0.0 ABP官方文档翻译目录
一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...
- ABP官方文档翻译 1.2 N层架构
N层架构 介绍 ABP架构 其他(通用) 领域层 应用层 基础设施层 网络和展现层 其他 总结 介绍 应用程序代码库的分层架构是被广泛认可的可以减少程序复杂度.提高代码复用率的技术.为了实现分层架构, ...
- ABP官方文档翻译 2.7 对象到对象的映射
对象到对象的映射 介绍 IObjectMapper接口 AutoMapper集成 安装 创建映射 自动映射属性 自定义映射 MapTo扩展方法 单元测试 预定义映射 LocalizeableStrin ...
- 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 ...
- 0.0 ABP官方文档翻译目录
一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...
- ABP官方文档翻译 2.5 设置管理
设置管理 介绍 关于 ISettingStore 定义设置 设置范围 重写设置定义 获取设置值 服务端 客户端 更改设置 关于缓存 介绍 每个应用都需要存储设置,并且在应用的某些地方需要使用这些设置. ...
随机推荐
- vijos 1213:80人环游世界
描述 想必大家都看过成龙大哥的<80天环游世界>,里面的紧张刺激的打斗场面一定给你留下了深刻的印象.现在就有这么一个80人的团伙,也想来一次环游世界. 他们打算兵分多路,游遍每一个国家. ...
- bzoj:3994:vijos1949: [SDOI2015]约数个数和
Description 设d(x)为x的约数个数,给定N.M,求 Input 输入文件包含多组测试数据. 第一行,一个整数T,表示测试数据的组数. 接下来的T行,每行两个整数N.M. O ...
- BZOJ 3195: [Jxoi2012]奇怪的道路(状压dp)
f[i][j][s]表示当前处理第i个点,前i-1个点已连j条边,第i个点开始k个点的奇偶性状态. #include<cstring>#include<algorithm>#i ...
- Linux shell编程命令-Linux基础环境命令学习笔记
1.正则表达式 1)^开始 *前一个字符重复0次以上 + 1次以上 ? 0次或者1次 . 一个任意字符(.*连用) {m,n} m到n次 [0-9][a-z] 任意数字或字母 $结束字符 2)sed和 ...
- 对于hive使用的一点记录
最近一段时间因工作需要接触了一些hive上的使用!当然大部分都是比较基本的使用,仅当入门!各位看到有不足之处望多多指正! 废话不多说,开始: 首先是创建数据库 create database '数据库 ...
- js获取不带单位的像素值
所谓获取不带单位的像素值就是获取比如元素的宽度.高度.字体大小.外边距.内边距等值但是去掉像素单位. 比如:某一个元素的宽度是100px,现在我要获取这个这个值但是不带单位“px”,对于这种问题你会怎 ...
- Spark算子--leftOuterJoin和rightOuterJoin
转载请标明出处http://www.cnblogs.com/haozhengfei/p/cb71cd3ac5d7965a2c61891659264d54.html leftOuterJoin和righ ...
- Flume介绍
Flume介绍 http://flume.apache.org/FlumeUserGuide.html 一.Flume架构图 含义 Source 规定收集数据的来源 Channel 相当于一个管道,连 ...
- Java-String.intern的深入研究
When---什么时候需要了解String的intern方法: 面试的时候(蜜汁尴尬)!虽然不想承认,不过面试的时候经常碰到这种高逼格的问题来考察我们是否真正理解了String的不可变性.String ...
- dede 内容页文章标题显示不全的更改方法
找到include/taglib/arclist.lib.php 1.$titlelen = AttDef($titlelen,30);换成$titlelen = AttDef($titlelen,2 ...