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 ...
随机推荐
- iOS---iOS10适配iOS当前所有系统的远程推送
一.iOS推送通知简介 众所周知苹果的推送通知从iOS3开始出现, 每一年都会更新一些新的用法. 譬如iOS7出现的Silent remote notifications(远程静默推送), iOS8出 ...
- Winserver下的Hyper-v “未在远程桌面会话中捕获到鼠标”
异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 服务器相关的知识点:http://www.cnblogs.com/dunitia ...
- Android混合开发之WebView与Javascript交互
前言: 最近公司的App为了加快开发效率选择了一部分功能采用H5开发,从目前市面的大部分App来讲,大致分成Native App.Web App.Hybrid App三种方式,个人觉得目前以Hybri ...
- JavaScript之职责链模式
一.概述 职责链模式(Chain of responsibility),就是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到有 ...
- 做一个gulp+webpack+vue的单页应用开发架子
1.目标 最近项目上的事情不多,根据我自己的开发习惯,决定开发一些简单的开发架子,方便以后事情多的时候直接套用.本文讲的一个gulp+webpack+vue的单页应用架子,想要达到的目的: 可以通过命 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- 使用cmake自动构建工程
公司引擎是用cmake根据目标平台来构建工程的,刚接触的时候深深体会到cmake的方便:如果目标平台是windows,它可以帮你自动构建出vs工程:如果是安卓,自动构建出eclipse工程,如果是IO ...
- TortoiseGit 文件比对工具使用 Beyond Compare 和 DiffMerge
TortoiseGit 内置的文件比对工具是 TortoiseMerge,用于文件比对和解决冲突合并,TortoiseGit 还支持外部的比对工具使用,比如 Beyond Compare 和 Diff ...
- Twproject Gantt开源甘特图功能扩展
1.Twproject Gantt甘特图介绍 Twproject Gantt 是一款基于 jQuery 开发的甘特图组件,也可以创建其它图表,例如任务树(Task Trees).内置编辑.缩放和 CS ...
- 星浩资本快速发展引擎:IT就是生产力
星浩资本成立于2010年,是一家涵盖私募基金.开发管理.商业与现代服务业三大业务范围的综合性管理公司,专注于投资中国首创.高成长性.高回报率的创新型城市综合体. 年轻的星浩资本在商业投资上有其独到的商 ...