ABP框架 - 应用服务
本节内容:
应用服务把领域逻辑暴露给展现层。一个应用服务被展现层使用一个DTO(数据传输对象)参数所调用,使用领域对象执行一些特殊业务逻辑并返回一个DTO给展现层。因此,展现层是完全独立于领域层,在一个理想的分层应用里,展现层从不直接使用领域对象。
IApplicationService 接口
在ABP里,一个应用服务应当实现IApplicationService接口,为每个应用服务创建一个接口是好的做法,所以我们先为一个应用服务创建一个接口,如下所示:
public interface IPersonAppService : IApplicationService
{
void CreatePerson(CreatePersonInput input);
}
IPersonAppService只有一个方法,它被展现层用来创建一个新的person,CreatePersonInput是一个DTO对象,如下所示:
public class CreatePersonInput
{
[Required]
public string Name { get; set; } public string EmailAddress { get; set; }
}
然后我们可以实现IPersonAppService:
public class PersonAppService : IPersonAppService
{
private readonly IRepository<Person> _personRepository; public PersonAppService(IRepository<Person> 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<Person>执行数据库操作,它使用构造器注入模式,这里我们使用依赖注入。
- PersonAppService实现IApplicationService(因为IPersonAppService扩展了IApplicationService),它自动被ABP注入到依赖注入系统,然后被其它类注入并使用。这里命名约定很重要,查看依赖注入文档获取更多信息。
- CreatePerson方法取得CreatePersonInput,它是一个输入的DTO并被ABP自动验证,更多细节查看DTO和验证文档。
ApplicationService 类
一个应用服务应当实现IApplicationService接口,如上所示,也可随意的继承于ApplicationService基类,因为它内部实现了IApplicationService,同时ApplicationService有些基本的功能,使得日志、本地化等更容易。建议为你的应用服务创建一个特殊的继承于ApplicationService类的基类,从而可以为所有应用服务添加一些通用的功能,一个应用服务示例如下:
public class TaskAppService : ApplicationService, ITaskAppService
{
public TaskAppService()
{
LocalizationSourceName = "SimpleTaskSystem";
} public void CreateTask(CreateTaskInput input)
{
//Write some logs (Logger is defined in ApplicationService class)
Logger.Info("Creating a new task with description: " + input.Description); //Get a localized text (L is a shortcut for LocalizationHelper.GetString(...), defined in ApplicationService class)
var text = L("SampleLocalizableTextKey"); //TODO: Add new task to database...
}
}
你有一个在构造器里定义了LocalizationSourceName的基类,这样,你就不用为所有的服务类重复定义了,这个主题的更多信息请查看日志和本地化文档。
CrudAppService 和 AsyncCrueAppService 类
如果你需要为一个特定的实体创建一个包含Create、Update、Delete、GetAll方法的应用服务,可以从CrudAppService(或AsyncCrudAppService,创建异步方法)继承,CrudAppService基类是一个以相关Entity和DTO类型为参数的泛型,并可通过重写功能进行你需要的定制。
简单的CRUD应用服务示例
假设你有一个Task实体,定义如下:
public class Task : Entity, IHasCreationTime
{
public string Title { get; set; } public string Description { get; set; } public DateTime CreationTime { get; set; } public TaskState State { get; set; } public Person AssignedPerson { get; set; }
public Guid? AssignedPersonId { get; set; } public Task()
{
CreationTime = Clock.Now;
State = TaskState.Open;
}
}
我们为该实体创建一个DTO:
[AutoMap(typeof(Task))]
public class TaskDto : EntityDto, IHasCreationTime
{
public string Title { get; set; } public string Description { get; set; } public DateTime CreationTime { get; set; } public TaskState State { get; set; } public Guid? AssignedPersonId { get; set; } public string AssignedPersonName { get; set; }
}
AutoMap特性创建实体与DTO之间的映射配置,现在,我们可以创建一个应用服务,如:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{ }
}
我们注入仓储并把它传给基类(如果要使用同步的方法,我们可以从CrudAppService继承)。这就是所有代码!TaskAppService现在就已经有了简单的CRUD方法,如果你想为这个应用服务创建一个接口,可以像下面这样:
public interface ITaskAppService : IAsyncCrudAppService<TaskDto>
{ }
注意:IAsyncCrudAppService没有以实体(Task)作为泛型参数,因为实体与实现相关,不应该包含在公开的接口里,接下来,我们就可以为TaskAppService类实现ITaskAppService接口:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto>, ITaskAppService
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{ }
}
定制CRUD应用服务
获取列表
Crud应用服务的GetAll方法默认以PagedAndSortedResultRequestInput为参数,该参数提供可选的排序和分页参数,但你可能想为GetAll方法添加另一个参数,例如:你想添加一些自定义过滤,这种情况,你可以为GetAll方法创建一个DTO,如:
public class GetAllTasksInput : PagedAndSortedResultRequestInput
{
public TaskState? State { get; set; }
}
我们从PagedAndSortedResultRequestInput继承(不是必须,但可以直接从基类得到paging和sorting参数),并添加一个可空的State属性,用来过滤task。现在我们应该修改TaskAppService,使它接受自定义过滤:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto, int, GetAllTasksInput>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{ } protected override IQueryable<Task> CreateFilteredQuery(GetAllTasksInput input)
{
return base.CreateFilteredQuery(input)
.WhereIf(input.State.HasValue, t => t.State == input.State.Value);
}
}
首先,我们添加GetAllTasksInputAsyncCrudAppService第4个泛型参数(第三个是实体主键的类型),然后重写CreateFilteredQuery方法实现自定义过滤,该方法是AsyncCrudAppService类的一个扩展点(WhereIf是ABP的一个扩展方法,用来简化条件过滤,但实质上我们所做的是简单过滤一个IQueryable)。
注意:如果你已创建应用服务接口,那么你就需要为接口添加相同的泛型参数。
创建和更新
注意:我们为获取、创建和更新Task使用相同的DTO(TaskDto),在现实应用里,这可能不太好,所以我们想定制创建和更新的DTO,让我们从创建一个CreateTaskInput类开始:
[AutoMapTo(typeof(Task))]
public class CreateTaskInput
{
[Required]
[MaxLength(Task.MaxTitleLength)]
public string Title { get; set; } [MaxLength(Task.MaxDescriptionLength)]
public string Description { get; set; } public Guid? AssignedPersonId { get; set; }
}
接着创建一个UpdateTaskInput DTO:
[AutoMapTo(typeof(Task))]
public class UpdateTaskInput : CreateTaskInput, IEntityDto
{
public int Id { get; set; } public TaskState State { get; set; }
}
我们为Update操作从CreateTaskInput上继承所有属性(但你也可以不这么做),此处必须实现实现IEntity(或不同于int类型的主键的IEntity<PrimaryKey>),因为我们需要知道哪一个实体需要更新,最后,我添加了一个额外的属性State,它不包含在CreateTaskInput里。
接下来,我们可以使用这些DTO类作为AsyncCrudAppService类的泛型接口,如下所示:
public class TaskAppService : AsyncCrudAppService<Task, TaskDto, int, GetAllTasksInput, CreateTaskInput, UpdateTaskInput>
{
public TaskAppService(IRepository<Task> repository)
: base(repository)
{ } protected override IQueryable<Task> CreateFilteredQuery(GetAllTasksInput input)
{
return base.CreateFilteredQuery(input)
.WhereIf(input.State.HasValue, t => t.State == input.State.Value);
}
}
不需要修改其它的代码。
其它
如果你想定制Get和Delete方法的input的DTO,AsyncCrudAppService可以接受更多的泛型参数。同样,所有的基类方法都是virtual,所以你可以重写它们进行行为定制。
工作单元
在ABP里一个应用服务方法就是一个工作单元,因此,任何一个应用服务方法都是事务性的,并自动在方法结束时保存修改到数据库。
更多信息查看工作单元文档。
一个应用服务的生命周期
所有应用服务实体都是Transient(短暂的),也就说:它们都是暂存于每次使用里。更多信息查看依赖注入文档。
ABP框架 - 应用服务的更多相关文章
- ABP入门系列(1)——学习Abp框架之实操演练
作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...
- ABP(现代ASP.NET样板开发框架)系列之15、ABP应用层——应用服务(Application services)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之15.ABP应用层——应用服务(Application services) ABP是“ASP.NET Boiler ...
- ABP框架 - 功能管理
文档目录 本节内容: 简介 关于 IFeatureValueStore 功能类型 Boolean 功能 Value 功能 定义功能 基本功能属性 其它功能属性 功能层次 检查功能 使用Requires ...
- [译]ABP框架使用AngularJs,ASP.NET MVC,Web API和EntityFramework构建N层架构的SPA应用程序
本文转自:http://www.skcode.cn/archives/281 本文演示ABP框架如何使用AngularJs,ASP.NET MVC,Web API 和EntityFramework构建 ...
- ABP框架详解(八)动态ApiController的生成和访问机制
在ABP框架中提供了一套动态生成ApiController的机制(依然支持原生ApiController的使用方式),虽然说是动态生成ApiController但是实际上并没有真正在启动程序的时候生成 ...
- 老周的ABP框架系列教程 -》 一、框架理论初步学习
老周的ABP框架系列教程 -- 一.框架理论初步学习 1. ABP框架的来源与作用简介 1.1 简介 1.1.1 ABP框架全称为"ASP.NET Boilerplate ...
- ABP应用层——应用服务(Application services)
ABP应用层——应用服务(Application services) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之15.ABP应用层——应用服务(Applic ...
- ABP框架 - N层架构
目录 介绍 DDD分层 ABP架构模型 客户端 展现层 分布式服务层 应用层 领域层 基础设施层 介绍 在应用程序设计中,分层架构是一种被广泛使用的技术,它助于降低复杂度和提高代码的可重用性.在ABP ...
- ABP框架 - 我的第一个Web API
本文示例源代码地址https://github.com/lcyhjx/abp-training 上一篇我们已经对ABP是什么,能做什么.有了一个印象.那么接下来我们将动手使用ABP框架快速开发一个AP ...
随机推荐
- [APUE]进程控制(上)
一.进程标识 进程ID 0是调度进程,常常被称为交换进程(swapper).该进程并不执行任何磁盘上的程序--它是内核的一部分,因此也被称为系统进程.进程ID 1是init进程,在自举(bootstr ...
- ASP.NET Aries 入门开发教程9:业务表单的开发
前言: 经过前面那么多篇的列表的介绍,终于到了大伙期待的表单开发了. 也是本系列的最后一篇文章了! 1:表单页面的权限设置与继承 对于表单页面,权限的设置有两种: 1:你可以选择添加菜单(设置为不显示 ...
- 【组织级项目管理】P2 MSP P3O
组织级项目管理--有你,有我,有大家 在过去的2年,无论对于企业来讲,还是对于我们个人都有很多大脑的冲击,有几个词大家应该特别耳熟能详:转型,变革,敏捷,互联网+,组织的项目化管理等.就是这些让我们的 ...
- 03.LoT.UI 前后台通用框架分解系列之——多样的表格
LOT.UI分解系列汇总:http://www.cnblogs.com/dunitian/p/4822808.html#lotui LoT.UI开源地址如下:https://github.com/du ...
- MySQL数据库和InnoDB存储引擎文件
参数文件 当MySQL示例启动时,数据库会先去读一个配置参数文件,用来寻找数据库的各种文件所在位置以及指定某些初始化参数,这些参数通常定义了某种内存结构有多大等.在默认情况下,MySQL实例会按照一定 ...
- 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...
- Android业务组件化之现状分析与探讨
前言: 从个人经历来说的话,从事APP开发这么多年来,所接触的APP的体积变得越来越大,业务的也变得越来越复杂,总来来说只有一句话:这是一个APP臃肿的时代!所以为了告别APP臃肿的时代,让我们进入一 ...
- ASP.NET Core 中文文档 第四章 MVC(3.9)视图组件
作者: Rick Anderson 翻译: 娄宇(Lyrics) 校对: 高嵩 章节: 介绍视图组件 创建视图组件 调用视图组件 演练:创建一个简单的视图组件 附加的资源 查看或下载示例代码 介绍视图 ...
- 使用HttpClient的优解
新工作入职不满半周,目前仍然还在交接工作,适应环境当中,笔者不得不说看别人的源码实在是令人痛苦.所幸今天终于将大部分工作流畅地看了一遍,接下来就是熟悉框架技术的阶段了. 也正是在看源码的过程当中,有一 ...
- JavaScript学习笔记(二)——闭包、IIFE、apply、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...