当前位置: 首页 > 编程日记 > 正文

ABP理论学习之数据传输对象(DTO)

本篇目录

  • 为何需要DTO
    • 领域层抽象
    • 数据隐藏
    • 序列化和懒加载问题
  • DTO惯例和验证
  • DTO和实体的自动映射
    • 使用特性和扩展方法进行映射
  • 帮助接口

DTO用于应用层和 展现层间的数据传输。

展现层调用具有DTO参数的应用服务方法,然后应用服务使用领域对象来执行一些特定的业务逻辑,最后返回给展现层一个DTO。因此,展现层完全独立于领域层。在一个理想的分层应用中,展现层不直接和领域对象打交道(仓储,实体...)。

为何需要DTO

为每个应用服务方法创建一个DTO起初可能被看作是一项乏味而又耗时的事情。但如果正确地使用它,那么DTOs可能会拯救你应用。为啥呢?

领域层抽象

DTO为展现层抽象领域对象提供了一种有效方式。这样,层与层之间就正确分离了。即使你想完全分离展现层,仍然可以使用已存在的应用层和领域层。相反,只要领域服务的契约(方法签名和DTOs)保持不变,即使重写领域层,完全改变数据库模式,实体和ORM框架,也不需要在展现层做任何改变。

数据隐藏

试想你有一个User实体,包含Id,Name,EmailAddress和Password字段。如果UserAppService的GetAllUsers()方法返回一个List,即使你没有在屏幕上显示它,那么任何人也都能看到所有user的密码。它不是涉及安全的,而是与数据隐藏相关的。应用服务都应该返回给展现层需要的,不要更多,也不很少,要的是恰到好处。

序列化和懒加载问题

当返回给展现层一个对象时,它很可能在某个地方序列化。比如,一个MVC方法返回JSON,一个对象会被序列化成JSON,然后发送到客户端。在那种情况,将一个实体返回到展现层是有问题的。这是怎么回事呢?

在一个真实应用中,实体之间是相互引用的。User实体可能有一个Role的引用。因此,如果你想序列化User,那么Role也会序列化。而且,如果Role有一个List且Permission类有一个PermissionGroup类的引用等等。你能想象所有的对象都会被序列化的那种场景吗?你可能会意外地序列化整个数据库。那么解决方案是什么呢?把属性标记为NonSerilized吗?不,你可能不知道它何时应该序列化,何时不应该。它可能在一个应用方法中需要,可能在另一个就不需要了。因此,在这种情景中,设计一个可安全序列化的,特别设计的DTOs是一种好的选择。

几乎所有的ORM框架都支持懒加载。它的特征是当需要时才从数据库中加载实体。假如说User类有一个Role类的引用。当从数据库中获得一个User时,此时Role属性还没有填充,当第一次读该Role属性时,它才从数据库中加载。因此,不要将这样的一个实体直接返回给展现层,它可能会轻易造成从数据库检索额外的实体。如果序列化工具读到了该实体,它会递归地读取所有属性,最终整个数据库可能会被检索(如果实体间有合适的关系)。

在展现层使用实体还会有更多的问题。最好压根不要在将包含领域(业务)层的程序集引用到展现层上。

DTO惯例和验证

ABP高度支持DTOs,它提供了一些符合惯例的类和接口,并且对于DTO的命名和用法提出了一些建议。当按照下面描述的那样编写代码时,ABP会轻易地自动处理一些事情。

举个例子

让我们看一个完整的例子。假如我们想要开发一个应用服务方法,作用是使用一个名字来搜索人,并返回一个人的集合。这种情况下,我们可能会有一个如下的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建议将input/output参数命名为MethodNameInput和 MethodNameOutput,并为每个应用服务方法定义一个单独的input和output DTO。即使你的方法只需要或返回一个参数,最好也创建一个DTO类。这样,你的代码回更具有扩展性。以后你可以添加更多的属性而不用改变方法的签名,而且也不用使已存在的客户端应用发生重大变化。

当然,如果你的方法没有返回值,那么方法可以返回void。如果以后添加了一个返回值,也不会打破已存在的应用。如果你的方法不需要任何参数,那么你也不必定义一个输入DTO。但是如果未来很可能添加参数,那么也许最好还是编写一个输入DTO。这取决于你。

让我们看一下为这个例子定义的输入和输出的DTO:

public class SearchPeopleInput : IInputDto
{[StringLength(40, MinimumLength = 1)]public string SearchedName { get; set; }
}public class SearchPeopleOutput : IOutputDto
{public List<PersonDto> People { get; set; }
}public class PersonDto : EntityDto
{public string Name { get; set; }public string EmailAddress { get; set; }
}

验证:按照惯例,输入DTO实现了 IInputDto接口,输出DTO实现了 IOutputDto接口。当实现了IInputDto时,ABP会在方法执行前自动验证输入。这和ASP.NET MVC的验证很相似,但是注意应用服务不是控制器,它是纯粹的C#类。ABP使用拦截来自动检查输入。关于更多的验证,请看下篇DTO验证。

EntityDto是一个声明了Id属性的简单类。因为这对于所有的实体都是公用的。如果你的实体的主键不是int的,那么还有一个泛型版本。PersonDto不包含Password属性,因为表现层不需要。甚至将所有人的密码都发送到展现层可能是很危险的。想象一下,如果Javascript客户端发送请求,任何人就会轻易地抓取到所有的密码。

接下来进一步实现之前的IPersonAppService

public class PersonAppService : IPersonAppService
{private readonly IPersonRepository _personRepository;public PersonAppService(IPersonRepository personRepository){_personRepository = personRepository;}public SearchPeopleOutput SearchPeople(SearchPeopleInput input){//Get entitiesvar peopleEntityList = _personRepository.GetAllList(person => person.Name.Contains(input.SearchedName));//Convert to DTOsvar peopleDtoList = peopleEntityList.Select(person => new PersonDto{Id = person.Id,Name = person.Name,EmailAddress = person.EmailAddress}).ToList();return new SearchPeopleOutput { People = peopleDtoList };}
}

我们从数据库中获得实体,再将它们转成DTOs,然后返回到输出。注意我们没有验证输入,因为ABP会自动验证。ABP甚至会检查输入参数是否为null,如果为null,就会抛出异常。

但是很可能你不喜欢从一个Person实体到一个PersonDto对象的转换代码。这是相当无聊的事情,而且,Person实体可能会有更多的属性。

DTO和实体的自动映射

幸好,我们有工具可以让这个变得很简单。AutoMapper就是之一(要学习AutoMapper,请看我的AutoMapper系列教程。它已经发布到Nuget上了,你可以轻松地将它添加到项目中。让我们再次写一下SearchPeople方法,但是这次是用AutoMapper:

public SearchPeopleOutput SearchPeople(SearchPeopleInput input)
{var peopleEntityList = _personRepository.GetAllList(person => person.Name.Contains(input.SearchedName));return new SearchPeopleOutput { People = Mapper.Map<List<PersonDto>>(peopleEntityList) };
}

这样就ok了。你可以给实体和DTO添加更多的属性而不需要转换代码做任何改变。唯一要做的事情就是在使用前定义一个映射:

Mapper.CreateMap<Person, PersonDto>();

AutoMapper创建了映射代码。这样,动态映射就不会成为性能问题了。它既快速又容易。AutoMapper为Person实体创建了PersonDto,并使用命名规范赋予DTO属性。命名规范可能是复杂的且可配置的。此外,你还可以定义自定义映射以及更多。

使用特性和扩展方法进行映射

ABP提供了若干特性和扩展方法来定义映射。首先,要将Abp.AutoMapper nuget包添加到项目中。然后,AutoMap特性是双向映射方式, AutoMapFrom和 AutoMapTo是单向映射方式。最后,使用MapTo扩展方法将一个对象映射到另一个对象。映射定义的例子如下:

[AutoMap(typeof(MyClass2))] //定义双向映射
public class MyClass1
{public string TestProp { get; set; }
}public class MyClass2
{public string TestProp { get; set; }
}

定义了上面的代码之后,就可以使用MapTo扩展方法映射它们了:

var obj1 = new MyClass1 { TestProp = "Test value" };
var obj2 = obj1.MapTo<MyClass2>(); //从obj1的副本创建一个新的MyClass2对象

上面的代码从MyClass1的对象创建了MyClass2一个新的对象。此外,你可以像下面那样,映射到一个已存在的对象:

var obj1 = new MyClass1 { TestProp = "Test value" };
var obj2 = new MyClass2();
obj1.MapTo(obj2);

帮助接口

ASP.NET 提供了一些实现标准化公共DTO属性名称的帮助接口。

ILimitedResultRequest定义了 MaxResultCount属性。这样你就可以在你的输入DTO中实现它来标准化有限的结果集。

IPagedResultRequest通过添加了 SkipCount扩展了 ILimitedResultRequest。这样,我们可以在SearchPeopleInput中为分页显示实现这个接口:

public class SearchPeopleInput : IInputDto, IPagedResultRequest
{[StringLength(40, MinimumLength = 1)]public string SearchedName { get; set; }public int MaxResultCount { get; set; }public int SkipCount { get; set; }
}

对于一个分页请求的结果,你可以返回一个实现了IHasTotalCount的输出DTO。命名标准化帮助我们创建可重复使用的代码和惯例。你也可以在 Abp.Application.Services.Dto命名空间下看到其他的接口和类。






本文转自tkbSimplest博客园博客,原文链接:http://www.cnblogs.com/farb/p/ABPDTO.html,如需转载请自行联系原作者

相关文章:

Linux如何查看当前目录下文件的个数

查看当前目录下文件的个数 ls -l | grep "^-" | wc -l 查看当前目录下文件的个数&#xff0c;包括子目录里的。 ls -lR| grep "^-" | wc -l 查看某目录下文件夹&#xff08;目录&#xff09;的个数&#xff0c;包括子目录里的。 ls -lR| grep "^d&quo…

迟语寒:组队学习的那些事

很开心又拿到了优秀船员&#xff0c;其实这已不是我第一次参加组队学习了&#xff0c;4月份的二手车是我第一次接触到DataWhale&#xff0c;那次课程结束后&#xff0c;马老师找到我让我分享一下心得&#xff0c;但是我觉得第一次坚持下来不算什么&#xff0c;于是我答应他如果…

Python培训教程分享:Python模块如何导入__all__属性?

本期小编为大家带来的Python培训教程是关于“Python模块如何导入__all__属性?”的内容&#xff0c;后面在工作中是会遇到Python模块这个工作内容的&#xff0c;Python模块的开头通常会定义一个__all__属性&#xff0c;该属性实际上是一个元组&#xff0c;该元组中包含的元素决…

F#探险之旅(三):命令式编程(上)

F#系列随笔索引页面 在本系列的第二部分&#xff08;函数式编程上、中、下&#xff09;中&#xff0c;我们了解了如何使用F#进行纯粹的函数式编程。但是在一些情况下&#xff0c;比如I/O&#xff0c;几乎不能避免改变状态&#xff0c;也就是说会带来side effect。F#并不强求你以…

Spring注解@Component和@resource的使用

http://heavengate.blog.163.com/blog/static/20238105320127234165489/转载于:https://www.cnblogs.com/passer1991/archive/2012/12/05/2802718.html

【青少年编程】黄羽恒:平行空间

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 微信后台回复“资料下载”可获取以往学习的材料&#xff08;视频、代码、文档&…

软件测试培训分享:软件测试岗位面试技巧有哪些?

在参加完软件测试培训后&#xff0c;我们最先要准备的就是Python的面试工作了&#xff0c;任何面试都是有技巧的&#xff0c;软件测试也不例外&#xff0c;那么下面小编就为大家详细的介绍一下软件测试岗位面试技巧有哪些?希望可以帮助到大家。 软件测试培训分享&#xff1a;软…

【青少年编程】黄羽恒:Python

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 微信后台回复“资料下载”可获取以往学习的材料&#xff08;视频、代码、文档&…

如何把文件隐藏在一张图片里面

该技巧适合 Windows 2000 / XP / Vista 1&#xff1a;准备一张图片&#xff0c;比如 jian.jpg。准备目标文件&#xff0c;比如dan.txt。我们要实现把dan.txt隐藏在图片里面。 2&#xff1a;打开命令行提示符&#xff1a;点击“开始→运行”&#xff0c;输入“cmd”。 3&#xf…

企业级nosql数据库应用与实战-redis

企业级nosql数据库应用与实战-redis项目场景&#xff1a;随着互联网2.0时代的发展&#xff0c;越来越多的公司更加注重用户体验和互动&#xff0c;这些公司的平台上会出现越来越多方便用户操作和选择的新功能&#xff0c;如优惠券发放、抢红包、购物车、热点新闻、购物排行榜等…

如何评判软件测试培训机构的好坏?

想要学习软件测试技术&#xff0c;那么找一家软件测试培训机构无疑是最好的选择&#xff0c;那么如今市面上的软件测试培训机构比较多&#xff0c;如何评判软件测试培训机构的好坏呢?来看看下面的详细介绍。 如何评判软件测试培训机构的好坏?现在国内的软件行业的市场日益增大…

Oracle 11g 新特性 -- Transparent Data Encryption (透明数据加密TDE) 增强 说明

一.TransparentData Encryption (TDE&#xff1a;透明数据加密) 说明Orace TDE 是Orcle 10R2中的一个新特性&#xff0c;其可以用来加密数据文件里的数据&#xff0c;保护从操作系统层面上对数据文件的访问。11g的TDE 在10gR2的基础上增强&#xff0c;允许在表空间级别进行加密…

[USACO07NOV]Cow Relays

mapfloyed矩阵乘法&#xff08;倍增floyed&#xff09; # include <stdio.h> # include <stdlib.h> # include <iostream> # include <algorithm> # include <string.h> # include <map> # define IL inline # define RG register # defin…

NCEPU:线下组队学习周报(011)

线下组队学习 经过一段时间的准备&#xff0c;我们组织的线下组队学习逐步进入正轨。欢迎华北电力大学保定校区的伙伴加入进来大家一起学习一起成长。 我们开展组队学习的内容为&#xff1a; &#xff08;1&#xff09;周志华的《机器学习》&#xff08;西瓜书&#xff09; …

java培训分享:学习java开发的优势是什么

想要进入到互联网行业的小伙伴&#xff0c;经常比较纠结学那个学科比较好&#xff0c;目前java、web前端、Python等都是非常热门的行业&#xff0c;前景也是比较好的&#xff0c;选择java学科的人比较多&#xff0c;那么学习java开发的优势是什么呢?来看看下面的详细介绍。 ja…

简述 clearfix 的原理

Demo Demo中的CSS中用到这样一个样式: .clearfix:after{content: ;display: block;clear: both; 复制代码 该样式通过::after选择器在选择元素之后插入content: 单引号中的内容&#xff0c;该内容不可被选定&#xff0c;例子中中插入内容为空。对这个空元素添加clear:both&…

判断dataset是否为空

最近在做一个搜索功能的时候,发现当用户搜索某些内容的时候,如果数据库有数据,就会显示在页面上.但是没有的时候呢,就一片空白,没有任何提示,觉得界面不友好.但是一时又不知道怎么判断dataset为空.网上找了一下,所以在这里写一下,方便自己以后复查.其实判断为空很简单,就这么写…

【Whalepaper】CV论文研读 - You Only Look One-level Feature (YOLOF), CVPR2021

Whalepaper是由周郴莲负责的一个每周分享论文的活动&#xff0c;带你研读AI领域的论文&#xff0c;快来一起开源学术科研吧&#xff01; NLP 论文分享&#xff1a;每周日 晚上 九点CV 论文分享&#xff1a; 每周日 晚上 九点Res 论文分享&#xff1a;每周六 晚上 九点半 欢迎…

Python培训分享:PyQT是什么?PyQt4和PyQt5的区别是什么?

今天小编为大家介绍的课程是关于Python培训方面的教程&#xff0c;主要讲的是PyQT是什么?PyQt4和PyQt5的区别是什么?来看看下面的详细介绍吧。 Python培训分享&#xff1a;PyQT是什么?PyQt4和PyQt5的区别是什么? QT是跨平台C库的集合&#xff0c;它实现高级API来访问现代桌…

linux 自学系列: 常用文件结构

/bin 二进制可执行命令  /dev 设备特殊文件  /etc 系统管理和配置文件  /etc/rc.d 启动的配置文件和脚本  /home 用户主目录&#xff0c;比如用户user的主目录就是/home/user&#xff0c;可以用&#xff5e;user表示  /lib 标准程序设计库&#xff08;动态链接共享库…

【青少年编程】【三级】海底寻宝

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 微信后台回复“资料下载”可获取以往学习的材料&#xff08;视频、代码、文档&…

iOS更改AppIcon

前言 iOS 10.3出了挺多的新功能&#xff0c;其中有一个就是可以自定义更改AppIcon。这个功能以前我们只能在苹果的时钟上面看出来。但是一直没有对外开放。现在开放出来&#xff0c;也算是一种突破。 实现 苹果在iOS 10.3中&#xff0c;多了一个setAlternateIconName:completio…

软件测试培训分享:做软件测试需要掌握数据库的知识吗?

最近几年&#xff0c;学习软件测试的同学越来越多&#xff0c;大家对于“做软件测试需要掌握数据库的知识吗?”这个问题都比较关注&#xff0c;那么下面小编就来为大家做下详细的介绍。 软件测试培训分享&#xff1a;做软件测试需要掌握数据库的知识吗?我们知道做开发、做运维…

enumeration学习

import org.junit.Test;public class enumeration { Test public void test() { System.out.print(School.name"--> "); System.out.print(School.name.getvalue()" :"); System.out.println(School.name.info()); }}enum School { name("名字&quo…

Cocoa pods的安装

使用下面命令不能移除原生的SOURCESgem sources --remove https://rubygems.org/报错如下source https://rubygems.org/ not present in cache究其原因&#xff0c;可是我上次更新的时候中断了解决方法&#xff0c;少敲一个/(这是什么鬼)gem sources --remove https://rubygems…

【青少年编程】黄羽恒:漫天飞雪

「青少年编程竞赛交流群」已成立&#xff08;适合6至18周岁的青少年&#xff09;&#xff0c;公众号后台回复【Scratch】或【Python】&#xff0c;即可进入。如果加入了之前的社群不需要重复加入。 微信后台回复“资料下载”可获取以往学习的材料&#xff08;视频、代码、文档&…

学习软件测试发展前景怎么样?有前途吗?

学习软件测试发展前景怎么样?有前途吗?很多人都比较关心这个问题&#xff0c;因为最近要学习软件测试技术的同学越来越多&#xff0c;大家对于今后的工作都比较迷茫&#xff0c;那么针对这个问题&#xff0c;来看看下面的详细介绍就知道了。 学习软件测试发展前景怎么样?有前…

Hello Jsilk

我们选择这里作为学习和分享技术知识的一个平台&#xff0c;希望大家共同进步&#xff01; 1 (function(){ 2 alert(Hello Jsilk); 3 })(); 转载于:https://www.cnblogs.com/jsilk/archive/2012/12/10/2811255.html

js 使用 Lawnchair 存储 json 对象到本地

项目中用到储存点击记录的功能。 Lawnchair是一个轻量级的移动应用程序数据持久化存储方案&#xff0c;同时也是客户端JSON文档存储方法&#xff0c;优点是短小&#xff0c;语法简洁&#xff0c;扩展性比较好。 现在做HTML5移动应用除了LocalStorage的兼容性比较好之外&#xf…

邓林权:组队学习的那些事

自我介绍 大家好&#xff0c;我是邓林权&#xff0c;职业是数据营销&#xff0c;工作是以数据为主&#xff0c;通过数据进行决策。平时喜欢研究数据&#xff0c;领略数据的魅力&#xff0c;很荣幸能够和大家分享我参加组队学习的收获和故事。 组队学习经历 今年4月开始参加组…