文档目录

本节内容:

应用服务把领域逻辑暴露给展现层。一个应用服务被展现层使用一个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框架 - 应用服务的更多相关文章

  1. ABP入门系列(1)——学习Abp框架之实操演练

    作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...

  2. ABP(现代ASP.NET样板开发框架)系列之15、ABP应用层——应用服务(Application services)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之15.ABP应用层——应用服务(Application services) ABP是“ASP.NET Boiler ...

  3. ABP框架 - 功能管理

    文档目录 本节内容: 简介 关于 IFeatureValueStore 功能类型 Boolean 功能 Value 功能 定义功能 基本功能属性 其它功能属性 功能层次 检查功能 使用Requires ...

  4. [译]ABP框架使用AngularJs,ASP.NET MVC,Web API和EntityFramework构建N层架构的SPA应用程序

    本文转自:http://www.skcode.cn/archives/281 本文演示ABP框架如何使用AngularJs,ASP.NET MVC,Web API 和EntityFramework构建 ...

  5. ABP框架详解(八)动态ApiController的生成和访问机制

    在ABP框架中提供了一套动态生成ApiController的机制(依然支持原生ApiController的使用方式),虽然说是动态生成ApiController但是实际上并没有真正在启动程序的时候生成 ...

  6. 老周的ABP框架系列教程 -》 一、框架理论初步学习

    老周的ABP框架系列教程 -- 一.框架理论初步学习   1. ABP框架的来源与作用简介 1.1  简介 1.1.1       ABP框架全称为"ASP.NET Boilerplate ...

  7. ABP应用层——应用服务(Application services)

    ABP应用层——应用服务(Application services) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之15.ABP应用层——应用服务(Applic ...

  8. ABP框架 - N层架构

    目录 介绍 DDD分层 ABP架构模型 客户端 展现层 分布式服务层 应用层 领域层 基础设施层 介绍 在应用程序设计中,分层架构是一种被广泛使用的技术,它助于降低复杂度和提高代码的可重用性.在ABP ...

  9. ABP框架 - 我的第一个Web API

    本文示例源代码地址https://github.com/lcyhjx/abp-training 上一篇我们已经对ABP是什么,能做什么.有了一个印象.那么接下来我们将动手使用ABP框架快速开发一个AP ...

随机推荐

  1. iOS---iOS10适配iOS当前所有系统的远程推送

    一.iOS推送通知简介 众所周知苹果的推送通知从iOS3开始出现, 每一年都会更新一些新的用法. 譬如iOS7出现的Silent remote notifications(远程静默推送), iOS8出 ...

  2. Winserver下的Hyper-v “未在远程桌面会话中捕获到鼠标”

    异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 服务器相关的知识点:http://www.cnblogs.com/dunitia ...

  3. Android混合开发之WebView与Javascript交互

    前言: 最近公司的App为了加快开发效率选择了一部分功能采用H5开发,从目前市面的大部分App来讲,大致分成Native App.Web App.Hybrid App三种方式,个人觉得目前以Hybri ...

  4. JavaScript之职责链模式

    一.概述 职责链模式(Chain of responsibility),就是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有 ...

  5. 做一个gulp+webpack+vue的单页应用开发架子

    1.目标 最近项目上的事情不多,根据我自己的开发习惯,决定开发一些简单的开发架子,方便以后事情多的时候直接套用.本文讲的一个gulp+webpack+vue的单页应用架子,想要达到的目的: 可以通过命 ...

  6. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  7. 使用cmake自动构建工程

    公司引擎是用cmake根据目标平台来构建工程的,刚接触的时候深深体会到cmake的方便:如果目标平台是windows,它可以帮你自动构建出vs工程:如果是安卓,自动构建出eclipse工程,如果是IO ...

  8. TortoiseGit 文件比对工具使用 Beyond Compare 和 DiffMerge

    TortoiseGit 内置的文件比对工具是 TortoiseMerge,用于文件比对和解决冲突合并,TortoiseGit 还支持外部的比对工具使用,比如 Beyond Compare 和 Diff ...

  9. Twproject Gantt开源甘特图功能扩展

    1.Twproject Gantt甘特图介绍 Twproject Gantt 是一款基于 jQuery 开发的甘特图组件,也可以创建其它图表,例如任务树(Task Trees).内置编辑.缩放和 CS ...

  10. 星浩资本快速发展引擎:IT就是生产力

    星浩资本成立于2010年,是一家涵盖私募基金.开发管理.商业与现代服务业三大业务范围的综合性管理公司,专注于投资中国首创.高成长性.高回报率的创新型城市综合体. 年轻的星浩资本在商业投资上有其独到的商 ...