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 定义设置 设置范围 重写设置定义 获取设置值 服务端 客户端 更改设置 关于缓存 介绍 每个应用都需要存储设置,并且在应用的某些地方需要使用这些设置. ...
随机推荐
- Codeforces 768A Oath of the Night's Watch
A. Oath of the Night's Watch time limit per test:2 seconds memory limit per test:256 megabytes input ...
- AtCoder Regular Contest 075
任意门 C - Bugged 题意:类似装箱问题,但是最后体积总和不能为10的倍数. #include<cstdio> #include<cstring> #include&l ...
- [51nod1457]小K vs. 竹子
小K的花园种着n颗竹子(竹子是一种茎部中空并且长得又高又快的热带植物).此时,花园中第i颗竹子的高度是hi米,并且在每天结束的时候它生长ai米. 实际上,小K十分讨厌这些竹子.他曾经试图去砍光它们,但 ...
- hdu_3483A Very Simple Problem(C(m,n)+快速幂矩阵)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3483 A Very Simple Problem Time Limit: 4000/2000 MS ( ...
- Java大数应用
1.大数加法 import java.math.BigInteger; import java.util.Scanner; public class Main { public static void ...
- JEECG 3.7.1 版本发布,企业级JAVA快速开发平台
JEECG 3.7.1 版本发布,企业级JAVA快速开发平台 ---------------------------------------- Version: Jeecg_3.7.1项 目: ...
- Sublime Text 使用介绍、全套快捷键及插件推荐
开篇:如果说Notepad++是一款不错Code神器,那么Sublime Text应当称得上是神器滴哥.Sublime Text最大的优点就是跨平台,Mac和Windows均可完美使用:其次是强大的插 ...
- Linux的软件安装(JDK安装,Mysql安装,Tomcat安装)
1.JDK安装 注意:rpm与软件相关命令 相当于window下的软件助手 管理软件 步骤: 1)查看当前Linux系统是否已经安装java 输入 rpm -qa | grep java ps:博主这 ...
- Android查缺补漏(View篇)--事件分发机制源码分析
在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)-- ...
- Java Enum解析【转】
Enum用法: 1:常量 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多 ...