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 定义设置 设置范围 重写设置定义 获取设置值 服务端 客户端 更改设置 关于缓存 介绍 每个应用都需要存储设置,并且在应用的某些地方需要使用这些设置. ...
随机推荐
- 三维dp&codeforce 369_2_C
三维dp&codeforce 369_2_C 标签: dp codeforce 369_2_C 题意: 一排树,初始的时候有的有颜色,有的没有颜色,现在给没有颜色的树染色,给出n课树,用m种燃 ...
- 部署Asp.net Core 项目发生502.5 或者500 没有其他提示信息
最近将公司原来.NetCore 1.6的项目升级到.net Core 2.0首先发生 502.5的错误,包括IIS日志,Windows应用程序日志都没有记录问题始终解决不了,首先看看官网给出的解决方案 ...
- [国嵌攻略][092][UDP网络程序设计]
server.c #include <sys/socket.h> #include <netinet/in.h> #include <strings.h> #inc ...
- POJ 2253 Frogger(Dijkstra变形——最短路径最大权值)
题目链接: http://poj.org/problem?id=2253 Description Freddy Frog is sitting on a stone in the middle of ...
- iframe及与页面之间的通信
获取iframe对象 iframe元素本身是位于父级页面中的,所以你可以像一个普通元素一样的使用和操作它 代表了iframe内容window对象是作为一个页面的属性加入到iframe中的, 为了让父级 ...
- 最近公司用到了lombok,感觉很不错的样子,所以上网搜了一些资料,总结了一下用法。
lombok作用:它提供了简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码,特别是相对于 POJO.缺点是使用lombok虽然能够省去手动创建setter和getter方法的麻烦, ...
- 解决:mysql is blocked because of many connection errors;
标签:because service foreign errors closed 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:// ...
- WIN2016安装织梦没写入权限怎么办听语音
配置好了WINSERVER2016环境,一切看起来都弄得差不多了,可是安装织梦的时候提示我没有写入权限,不能继续安装,于是我很郁闷,开始寻求解决办法. 工具/原料 WINSERVER2016 织梦5. ...
- two Pass方法连通域检测
原理: Two-Pass方法检测连通域的原理可参见这篇博客:http://blog.csdn.net/lichengyu/article/details/13986521. 参考下面动图,一目了然. ...
- UML图学习之三 状态图
状态图(Statechart Diagram)主要用于描述一个对象在其生存期间的动态行为,表现为一个对象所经历的状态序列,引起状态转移的事件(Event),以及因状态转移而伴随的动作(Action). ...