千家信息网

ABP框架中数据传输对象与应用服务的示例分析

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要介绍了ABP框架中数据传输对象与应用服务的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。数据传输对象(DTOs)数
千家信息网最后更新 2026年11月01日ABP框架中数据传输对象与应用服务的示例分析

这篇文章主要介绍了ABP框架中数据传输对象与应用服务的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

数据传输对象(DTOs)
数据传输对象(Data Transfer Objects)用于应用层和展现层的数据传输。

展现层传入数据传输对象(DTO)调用一个应用服务方法,接着应用服务通过领域对象执行一些特定的业务逻辑并且返回DTO给展现层。这样展现层和领域层被完全分离开了。在具有良好分层的应用程序中,展现层不会直接使用领域对象(仓库,实体)。

1.数据传输对象的作用:
为每个应用服务方法创建DTO看起来是一项乏味耗时的工作。但如果你正确使用它们,这将会解救你的项目。为啥呢?

(1)抽象领域层 (Abstraction of domain layer)

在展现层中数据传输对象对领域对象进行了有效的抽象。这样你的层(layers)将被恰当的隔离开来。甚至当你想要完全替换展现层时,你还可以继续使用已经存在的应用层和领域层。反之,你可以重写领域层,修改数据库结构,实体和ORM框架,但并不需要对展现层做任何修改,只要你的应用层没有发生改变。

(2)数据隐藏 (Data hiding)

想象一下,你有一个User实体拥有属性Id, Name, EmailAddress和Password。如果UserAppService的GetAllUsers()方法的返回值类型为List。这样任何人都可以查看所有人的密码,即使你没有将它打印在屏幕上。这不仅仅是安全问题,这还跟数据隐藏有关。应用服务应只返回展现层所需要的,不多不少刚刚好。

(3)序列化 & 惰性加载 (Serialization & lazy load problems)

当你将数据(对象)返回给展现层时,数据有可能会被序列化。举个例子,在一个返回Json的MVC的Action中,你的对象需要被序列化成JSON并发送给客户端。直接返回实体给展现层将有可能会出现麻烦。

在真实的项目中,实体会引用其他实体。User实体会引用Role实体。所以,当你序列化User时,Role也将被序列化。而且Role还拥有一个List并且Permission还引用了PermissionGroup等等….你能想象这些对象都将被序列化吗?这有很有可能使整个数据库数据意外的被序列化。那么该如何解决呢?将属性标记为不可序列化?不行,因为你不知道属性何时该被序列化何时不该序列化。所以在这种情况下,返回一个可安全序列化,特别定制的数据传输对象是不错的选择哦。

几乎所有的ORM框架都支持惰性加载。只有当你需要加载实体时它才会被加载。比如User类型引用Role类型。当你从数据库获取User时,Role属性并没有被填充。当你第一次读取Role属性时,才会从数据库中加载Role。所以,当你返回这样一个实体给展现层时,很容易引起副作用(从数据库中加载)。如果序列化工具读取实体,它将会递归地读取所有属性,这样你的整个数据库都将会被读取。

在展现层中使用实体还会有更多的问题。最佳的方案就是展现层不应该引用任何包含领域层的程序集。

2.DTO 约定 & 验证
ABP对数据传输对象提供了强大的支持。它提供了一些相关的(Conventional)类型 & 接口并对DTO命名和使用约定提供了建议。当你像这里一样使用DTO,ABP将会自动化一些任务使你更加轻松。

一个例子 (Example)

让我们来看一个完整的例子。我们相要编写一个应用服务方法根据name来搜索people并返回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建议命名input/ouput对象类似于MethodNameInput/MethodNameOutput,对于每个应用服务方法都需要将Input和Output进行分开定义。甚至你的方法只接收或者返回一个值,也最好创建相应的DTO类型。这样,你的代码才会更具有扩展性,你可以添加更多的属性而不需要更改方法的签名,这并不会破坏现有的客户端应用。

当然,方法返回值有可能是void,之后你添加一个返回值并不会破坏现有的应用。如果你的方法不需要任何参数,那么你不需要定义一个Input Dto。但是创建一个Input Dto可能是个更好的方案,因为该方法在将来有可能会需要一个参数。当然是否创建这取决于你。 Input和Output DTO类型定义如下:

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

验证:作为约定,Input DTO实现IInputDto 接口,Output DTO实现IOutputDto接口。当你声明IInputDto参数时, 在方法执行前ABP将会自动对其进行有效性验证。这类似于ASP.NET MVC验证机制,但是请注意应用服务并不是一个控制器(Controller)。ABP对其进行拦截并检查输入。查看DTO 验证(DTO Validation)文档获取更多信息。 EntityDto是一个简单具有与实体相同的Id属性的简单类型。如果你的实体Id不为int型你可以使用它泛型版本。EntityDto也实现了IDto接口。你可以看到PersonDto并不包含Password属性,因为展现层并不需要它。

跟进一步之前我们先实现IPersonAppService:

public class PersonAppService : IPersonAppService{  private readonly IPersonRepository _personRepository;  public PersonAppService(IPersonRepository personRepository)  {    _personRepository = personRepository;  }  public SearchPeopleOutput SearchPeople(SearchPeopleInput input)  {    //获取实体    var peopleEntityList = _personRepository.GetAllList(person => person.Name.Contains(input.SearchedName));    //转换成DTO    var peopleDtoList = peopleEntityList      .Select(person => new PersonDto                {                  Id = person.Id,                  Name = person.Name,                  EmailAddress = person.EmailAddress                }).ToList();    return new SearchPeopleOutput { People = peopleDtoList };  }}


我们从数据库获取实体,将实体转换成DTO并返回output。注意我们没有手动检测Input的数据有效性。ABP会自动验证它。ABP甚至会检查Input是否为null,如果为null则会抛出异常。这避免了我们在每个方法中都手动检查数据有效性。

但是你很可能不喜欢手动将Person实体转换成PersonDto。这真的是个乏味的工作。Peson实体包含大量属性时更是如此。

3.DTO和实体间的自动映射
还好这里有些工具可以让映射(转换)变得十分简单。AutoMapper就是其中之一。你可以通过nuget把它添加到你的项目中。让我们使用AutoMapper来重写SearchPeople方法:

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

这就是全部代码。你可以在实体和DTO中添加更多的属性,但是转换代码依然保持不变。在这之前你只需要做一件事:映射

Mapper.CreateMap();

AutoMapper创建了映射的代码。这样,动态映射就不会成为性能问题。真是快速又方便。AutoMapper根据Person实体创建了PersonDto,并根据命名约定来给PersonDto的属性赋值。命名约定是可配置的并且很灵活。你也可以自定义映射和使用更多特性,查看AutoMapper的文档获取更多信息。

4.使用特性(attributes)和扩展方法来映射 (Mapping using attributes and extension methods)

ABP提供了几种attributes和扩展方法来定义映射。使用它你需要通过nuget将Abp.AutoMapper添加到你的项目中。使用AutoMap特性(attribute)可以有两种方式进行映射,一种是使用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.TestProp的值赋值给新的MyClass2对象的TestProp属性。上面的代码根据MyClass1创建了新的MyClass2对象。你也可以映射已存在的对象,如下所示:var obj1 = new MyClass1 { TestProp = "Test value" };var obj2 = new MyClass2();obj1.MapTo(obj2); //根据obj1设置obj2的属性

5.辅助接口和类型
ABP还提供了一些辅助接口,定义了常用的标准化属性。

ILimitedResultRequest定义了MaxResultCount属性。所以你可以在你的Input DTO上实现该接口来限制结果集数量。

IPagedResultRequest扩展了ILimitedResultRequest,它添加了SkipCount属性。所以我们在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的Output DTO作为返回结果。标准化属性帮助我们创建可复用的代码和规范。可在Abp.Application.Services.Dto命名空间下查看其他的接口和类型。

应用服务
应用服务用于将领域(业务)逻辑暴露给展现层。展现层通过传入DTO(数据传输对象)参数来调用应用服务,而应用服务通过领域对象来执行相应的业务逻辑并且将DTO返回给展现层。因此,展现层和领域层将被完全隔离开来。在一个理想的层级项目中,展现层应该从不直接访问领域对象。

1.IApplicationService接口
在ABP中,一个应用服务需要实现IApplicationService接口。最好的实践是针对每个应用服务都创建相应的接口。所以,我们首先定义一个应用服务接口,如下所示:

public interface IPersonAppService : IApplicationService{  void CreatePerson(CreatePersonInput input);}

IPersonAppService只有一个方法,它将被展现层调用来创建一个新的Person。CreatePersonInput是一个DTO对象,如下所示:


public class CreatePersonInput : IInputDto{  [Required]  public string Name { get; set; }  public string EmailAddress { get; set; }}

接着,我们实现IPersonAppService接口:

public class PersonAppService : IPersonAppService{  private readonly IRepository _personRepository;  public PersonAppService(IRepository personRepository)  {    _personRepository = personRepository;  }  public void CreatePerson(CreatePersonInput input)  {    var person = _personRepository.FirstOrDefault(p => p.EmailAddress == input.EmailAddress);    if (person != null)    {      throw new UserFriendlyException("There is already a person with given email address");    }    person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };    _personRepository.Insert(person);  }}


以下是几个重要提示:

  • PersonAppService通过IRepository来执行数据库操作。它通过构造器注入模式来生成。我们在这里使用了依赖注入。

  • PersonAppService实现了IApplicationService(通过IPersonAppService继承IApplicationService)。ABP会自动地把它注册到依赖注入系统中,并可以注入到别的类型中使用。

  • CreatePerson方法需要一个CreatePersonInput类型的参数。这是一个作为输入的DTO,它将被ABP自动验证其数据有效性。可以查看DTO和数据有效性验证(Validation)文档获取相关细节。

2.应用服务类型

应用服务(Application Services)需要实现IApplicationService接口。当然,你可以选择将你的应用服务(Application Services)继承自ApplicationService基类,这样你的应用服务也就自然而然的实现IApplicationService接口了。ApplicationService基类提供了方便的日志记录和本地化功能。在此建议你针对你的应用程序创建一个应用服务基类继承自ApplicationService类型。这样你就可以添加一些公共的功能来提供给你的所有应用服务使用。一个应用服务示例如下所示:

public class TaskAppService : ApplicationService, ITaskAppService{  public TaskAppService()  {    LocalizationSourceName = "SimpleTaskSystem";  }  public void CreateTask(CreateTaskInput input)  {    //记录日志,Logger定义在ApplicationService中    Logger.Info("Creating a new task with description: " + input.Description);    //获取本地化文本(L是LocalizationHelper.GetString(...)的简便版本, 定义在 ApplicationService类型)    var text = L("SampleLocalizableTextKey");    //TODO: Add new task to database...  }}

本例中我们在构造函数中定义了LocalizationSourceName,但你可以在基类中定义它,这样你就不需要在每个具体的应用服务中定义它。查看日志记录(logging)和本地化(localization)文档可以获取更多的相关信息。

感谢你能够认真阅读完这篇文章,希望小编分享的"ABP框架中数据传输对象与应用服务的示例分析"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

应用 数据 应用服务 服务 对象 实体 方法 属性 接口 类型 序列 领域 数据传输 传输 数据库 更多 验证 代码 有效 参数 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技 苏州找大状互联网科技有限公司 唐信互联网科技 大连 上海亿帐通互联网科技 佩信高科技互联网 互联网科技产品 互联网时代科技改变生活 杭州壁岐互联网科技有限公司 天津腾达互联网科技有限公司 互联网科技如何创业 南博望互联网科技有限公司 中和天地互联网科技有限公司 合肥互联网科技公司公章图片 广东工业互联网大赛深圳学泰科技 互联网相关科技技术 上海创道互联网络科技有限公司 广州互联网高科技取餐柜 互联网科技创业计划的风险分析 科技互联网企业在行动 互联网科技快速发展优美句子 华服互联网科技靠谱吗 没上市的科技互联网公司 浩特互联网科技 剑昆互联网络科技有限公司临桂 互联网科技的全部知识 广州百诚互联网科技有限公司官网 郑州欣桑互联网科技有限公司 宁波跨境商互联网科技有限公司 湖北辰鑫科技互联网 北京互联网科技类企业分布
0