ABP理论学习之应用服务
本篇目录
应用服务用于将领域逻辑暴露给展现层。展现层调用具有DTO参数的应用服务,使用领域对象来执行一些特定的业务逻辑并返回给展现层一个DTO。这样,展现层就完全独立于领域层了。在一个理想的分层应用中,展现层永远不直接和领域对象打交道。
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<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执行数据库操作。这里使用了依赖注入,而且使用了 构造函数注入的模式。
- PersonAppService实现了IApplicationService,它是通过ABP自动注册到依赖注入系统的,然后被其他的类注入并使用。
- CreatePerson方法以 CreatePersonInput作为参数。它是一个输入DTO,会被ABP自动验证。
ApplicationService类
应用服务类应该实现应用服务接口(IApplicationService)。此外,还可以选择从ApplicationService基类派生。这样,IApplicationService也被继承实现了。而且,ApplicationService有一些基本功能,使得logging和 本土化更加简单。建议为你的继承了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。这样,就不用为所有的服务类重复定义了。
工作单元###
在ABP中,应用服务方法默认是一个工作单元。
数据库连接和事务管理
假如说我们想要在一个必须是事务的应用服务方法中调用两个仓储方法。例如:
public void CreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount();
}
我们将一个person实体插入到People表中,然后总人数自增,并保存到另一个表的字段中。这个方法都是用不同的仓储实现的,但是共享了相同的连接和事务。
在CreatePerson方法开始时,ABP会自动打开数据库连接并开始事务。在方法结束时,如果没有异常发生,会自动提交事务并关闭数据库连接。这样,在CreatePerson方法中的所有数据库操作都是事务的(原子的),如果有任何异常抛出,操作就会回滚。因此,在这个方法中的两个仓储共享相同的连接和事务。
当调用仓储的GetAll()方法时,会返回一个IQueryable。数据库连接应该在调用该方法后打开。这是因为IQueryable和Linq会延迟执行。数据库真正查询是在调用 ToList()方法时发生的。看下面的例子:
public SearchPeopleOutput SearchPeople(SearchPeopleInput input)
{
//获得 IQueryable<Person>
var query = _personRepository.GetAll();
//添加过滤
if (!string.IsNullOrEmpty(input.SearchedName))
{
query = query.Where(person => person.Name.StartsWith(input.SearchedName));
}
if (input.IsActive.HasValue)
{
query = query.Where(person => person.IsActive == input.IsActive.Value);
}
//获取分页结果list
var people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
return new SearchPeopleOutput {People = Mapper.Map<List<PersonDto>>(people)};
}
因为一个应用服务方法是一个工作单元,所以在执行这个方法期间数据库连接是打开的。如果在一个不是应用服务的类中调用了GetAll()方法,那么应该显式使用工作单元。
注意这里使用了AutoMapper类库将 List转成List。更多细节请看下一篇DTO博客。
自动保存更改
对于工作单元方法,ABP会在方法结束时自动保存所有的更改。假设我们有一个更新一个人的名字的应用服务方法:
public void UpdateName(UpdateNameInput input)
{
var person = _personRepository.Get(input.PersonId);
person.Name = input.NewName;
}
只需要这样,name字段就改变了。我们甚至都不要调用_personRepository.Update方法。ORM框架会跟踪工作单元内的实体的所有更改,并将更改反映给数据库。
更多
请查看《工作单元》。
应用服务的生命周期###
所有的应用服务实例都是Transient(每次使用时创建)。ABP强烈建议使用依赖注入技术。当一个应用服务类需要注入时,该类的一个新实例会在依赖注入容器中自动创建。更多内容请查看《依赖注入》。
ABP理论学习之应用服务的更多相关文章
- ABP理论学习之Javascript API(理论完结篇)
返回总目录 本篇目录 Ajax Notification Message UI block和busy 事件总线 Logging 其他工具功能 说在前面的话 不知不觉,我们送走了2015,同时迎来了20 ...
- ABP理论学习之开篇介绍
返回总目录 为了和2016年春节赛跑,完成该系列博客,我牺牲了今天中午的时间来完成该系列的第一篇----开篇介绍.开篇介绍嘛,读过大学教材的同学都知道,这玩意总是那么无聊,跟考试没关系,干脆直接跳过, ...
- ABP理论学习之N层架构
返回总目录 自从写这个系列博客之后,发现很多园友还是希望有个直接运行的demo,其实在github上就有官方的demo,我直接把这demo的链接放到这里吧,另外,我分析,这些找不到demo的同学,很可 ...
- ABP理论学习之依赖注入
返回总目录 本篇目录 什么是依赖注入 传统方式产生的问题 解决办法 依赖注入框架 ABP中的依赖注入基础设施 注册 解析 其他 ASP.NET MVC和ASP.NET Web API集成 最后提示 什 ...
- ABP理论学习之日志记录
返回总目录 本篇目录 服务端 获取Logger 基类中的Logger 配置 客户端 服务端 ABP使用的是Castle Windsor的日志记录设备.它可以和不同的日志类库一起工作,比如Log4Net ...
- ABP理论学习之仓储
返回总目录 本篇目录 IRepository接口 查询 插入 更新 删除 其他 关于异步方法 仓储实现 管理数据库连接 仓储的生命周期 仓储最佳实践 Martin Fowler对仓储的定义 位于领域层 ...
- ABP理论学习之领域服务
返回总目录 本篇目录 介绍 IDomainService接口和DomainService类 样例 创建一个接口 服务实现 调用应用服务 一些讨论 何不只使用应用服务 如何强制使用领域服务 介绍 领域服 ...
- ABP理论学习之工作单元(Unit of Work)
返回总目录 本篇目录 公共连接和事务管理方法 ABP中的连接和事务管理 仓储类 应用服务 工作单元 工作单元详解 关闭工作单元 非事务的工作单元 工作单元方法调用其它 工作单元作用域 自动保存 IRe ...
- ABP理论学习之数据过滤器
返回总目录 本篇目录 介绍 预定义过滤器 关闭过滤器 开启过滤器 设置过滤器参数 定义自定义过滤器 其他ORM 介绍 软删除模式通常用于不会真正从数据库删除一个实体而是仅仅将它标记为"已删除 ...
随机推荐
- thinkphp 3.2.3 session 丟失問題
之前做的几个 站session在跨页时也不会丢失(都在同一台服务器,所以我排除了服务器配置问题),这次居然很奇怪的发生的,在火狐上有,在ie, 谷哥上没有session,看了很多网上的贴子 其中有一个 ...
- [Tool]Inno Setup创建软件安装程序。
这篇博客将介绍如何使用Inno Setup创建一个软件安装程序. Inno Setup官网:http://www.jrsoftware.org/isinfo.php. 可以下载到最新的Inno Set ...
- Python-基础数据类型
数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等各种各样的数据,不同的数据,需要定 ...
- LinkedHashMap源码阅读笔记(基于jdk1.8)
LinkedHashMap是HashMap的子类,很多地方都是直接引用HashMap中的方法,所以需要注意的地方并不多.关键的点就是几个重写的方法: 1.Entry是继承与Node类,也就是Linke ...
- 了解 Spring Data JPA
前言 自 JPA 伴随 Java EE 5 发布以来,受到了各大厂商及开源社区的追捧,各种商用的和开源的 JPA 框架如雨后春笋般出现,为开发者提供了丰富的选择.它一改之前 EJB 2.x 中实体 B ...
- VadRoot枚举进程模块在Windows7下的完整实现
原理小伟的小伟在http://bbs.pediy.com/showthread.php?t=66886说的挺清楚了,Windows7下有一些变化,使用NtQueryVirtualMemory来枚举 ...
- web.xml文件加载顺序
1.启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点. 2.紧急着,容创建一个Servl ...
- If & Else 语句
If 语句 user= "吹Sir"passwd= "strong"username = input ("Username:")passwo ...
- Effective C++ 笔记2(构造,析构,赋值)
条款5:了解C++默默编写并且调用了哪些函数 1. 构造函数,析构函数,拷贝赋值函数,拷贝构造函数. class Empty { public: //默认构造函数 Empty(){}; //拷贝构造 ...
- a 标签中加 onclick事件,根据事件中的校验情况来决定是否执行a标签的链接
a 标签中加 onclick方法后,先执行onclick方法,在去执行a标签href下属性对应的动作,如果不想执行href属性下动作需要用false作为返回值. <a href="ht ...