点这里进入ABP系列文章总目录

基于DDD的现代ASP.NET开发框架--ABP系列之15、ABP应用层——应用服务(Application services)

ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。

ABP的官方网站http://www.aspnetboilerplate.com

ABP在Github上的开源项目https://github.com/aspnetboilerplate

本文由东莞-天道提供翻译


应用服务用于将领域(业务)逻辑暴露给展现层。展现层通过传入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(通过IPersonAppService继承IApplicationService)。ABP会自动地把它注册到依赖注入系统中,并可以注入到别的类型中使用。
  • CreatePerson方法需要一个CreatePersonInput类型的参数。这是一个作为输入的DTO,它将被ABP自动验证其数据有效性。可以查看DTO和数据有效性验证(Validation)文档获取相关细节。

应用服务类型

应用服务(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中,一个应用服务方法默认是一个工作单元。

(1)连接 & 事务管理 (For connection & transaction management)

在应用服务方法中,如果我们需要调用两个仓储方法,那么这些方法必须为一个事务。举个例子:

public void CreatePerson(CreatePersonInput input)
{
var person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
_personRepository.Insert(person);
_statisticsRepository.IncrementPeopleCount();
}

我们向Person表插入一个数据,接着在其他表中修改了Person计数字段的值。这两个操作实现于不同的仓储中,但是它们使用了相同的数据连接和事务。这是怎么实现的呢?

对于UOW模式,当事务启动并且开始执行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);
} //获取分页
var people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList(); return new SearchPeopleOutput {People = Mapper.Map<List<PersonDto>>(people)};
}

由于一个应用服务(Application Services)方法就是一个工作单元,所以数据库连接在方法执行期间都是开启的。如果你在非应用服务(Application Services)中调用GetAll(),你需要显式的使用工作单元模式。如:在Controller的Action方法中要使用GetAll()或调用多个有对数据库操作的AppService方法时, 应该将Action方法使用virtual修饰,并在Action的上面通过[UnitOfWork]进行显示开启工作单元模式。

注意我使用了AutoMapper库将List转换成List。可以查看DTO文档获取相关细节。

译者-天道注:这里要说一下,就是uow和非uow模式的区别,两种模式对于数据库连接的打开和关闭是不同的。对于控制器的方法,ABP默认是非 uow模式,此时如果调用方法会报错,提示数据库未连接。解决的办法是在方法加上virtual。

(2)自动保存数据修改 (For automatically saving changes)

对于工作单元方法(应用服务(Application Services)方法),在方法结束时ABP将会自动保存所有数据修改。假设我们需要一个应用服务(Application Services)方法来更新一个Person的Name:

public void UpdateName(UpdateNameInput input)
{
var person = _personRepository.Get(input.PersonId);
person.Name = input.NewName;
}

就是这样,Name被成功修改!我们甚至不需要调用_personRepository.Update方法。ORM框架在工作单元中会跟踪所有实体修改并将修改更新到数据库中。

应用服务的生命周期

所有应用服务(Application Services)实例的生命周期都是暂时的(Transient)。这意味着在每次使用都会创建新的应用服务(Application Services)实例。ABP坚决地使用依赖注入技术。当一个应用服务(Application Services)类型需要被注入时,该应用服务(Application Services)类型的新实例将会被依赖注入容器自动创建。查看依赖注入(Dependency Injection)文档获取更多信息。


希望更多国内的架构师能关注到ABP这个项目,也许这其中有能帮助到您的地方,也许有您的参与,这个项目可以发展得更好。

欢迎加QQ群:

ABP架构设计交流群:134710707       ABP架构设计交流2群: 579765441

点这里进入ABP系列文章总目录

ABP(现代ASP.NET样板开发框架)系列之15、ABP应用层——应用服务(Application services)的更多相关文章

  1. ABP(现代ASP.NET样板开发框架)系列之18、ABP应用层——权限验证

    点这里进入ABP系列文章总目录 ABP(现代ASP.NET样板开发框架)系列之18.ABP应用层——权限验证 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目 ...

  2. ABP(现代ASP.NET样板开发框架)系列之20、ABP展现层——动态生成WebApi

    点这里进入ABP系列文章总目录 ABP(现代ASP.NET样板开发框架)系列之20.ABP展现层——动态生成WebApi ABP是“ASP.NET Boilerplate Project (ASP.N ...

  3. ABP(现代ASP.NET样板开发框架)系列之3、ABP分层架构

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之3.ABP分层架构 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  4. ABP(现代ASP.NET样板开发框架)系列之4、ABP模块系统

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之4.ABP模块系统 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  5. ABP(现代ASP.NET样板开发框架)系列之5、ABP启动配置

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之5.ABP启动配置 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  6. ABP(现代ASP.NET样板开发框架)系列之6、ABP依赖注入

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之6.ABP依赖注入 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  7. ABP(现代ASP.NET样板开发框架)系列之7、ABP Session管理

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之7.ABP Session管理 ABP是“ASP.NET Boilerplate Project (ASP.NET ...

  8. ABP(现代ASP.NET样板开发框架)系列之8、ABP日志管理

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之8.ABP日志管理 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  9. ABP(现代ASP.NET样板开发框架)系列之9、ABP设置管理

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之9.ABP设置管理 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

随机推荐

  1. 谈谈一些有趣的CSS题目(十二)-- 你该知道的字体 font-family

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  2. ifconfig: command not found(CentOS专版,其他的可以参考)

    ifconfig: command not found 查看path配置(echo相当于c中的printf,C#中的Console.WriteLine) echo $PATH 解决方案1:先看看是不是 ...

  3. HTML5 localStorage本地存储

    介绍 localStorage(本地存储)的使用方式.包括对存储对象的添加.修改.删除.事件触发等操作. 目录 1. 介绍 1.1 说明 1.2 特点 1.3 浏览器最小版本支持 1.4 适合场景 2 ...

  4. 深入理解MySql子查询IN的执行和优化

    IN为什么慢? 在应用程序中使用子查询后,SQL语句的查询性能变得非常糟糕.例如: SELECT driver_id FROM driver where driver_id in (SELECT dr ...

  5. 如何进行python性能分析?

    在分析python代码性能瓶颈,但又不想修改源代码的时候,ipython shell以及第三方库提供了很多扩展工具,可以不用在代码里面加上统计性能的装饰器,也能很方便直观的分析代码性能.下面以我自己实 ...

  6. 菜鸟Python学习笔记第二天:关于Python黑客。

    2016年1月5日 星期四 天气:还好 一直不知道自己为什么要去学Python,其实Python能做到的Java都可以做到,Python有的有点Java也有,而且Java还是必修课,可是就是不愿意去学 ...

  7. 深入解析Sqlite的完美替代者,android数据库新王者——Realm

    写在前面: 又到一年一度七夕虐狗节,看着大家忍受着各种朋友圈和QQ空间还有现实生活中的轮番轰炸,我实在不忍心再在这里给大家补刀,所以我觉得今天不虐狗,继续给大家分享有用的. 如果你比较关心androi ...

  8. jquery学习(一)

    简单的jquery学习,首先在页面引入jquery <!-- 引入jquery --> <script src="js/jquery-1.8.3.js" type ...

  9. Android中常见的图片加载框架

    图片加载涉及到图片的缓存.图片的处理.图片的显示等.而随着市面上手机设备的硬件水平飞速发展,对图片的显示要求越来越高,稍微处理不好就会造成内存溢出等问题.很多软件厂家的通用做法就是借用第三方的框架进行 ...

  10. phpexcel读取输出操作

    //读取 <?php header("Content-Type:text/html;charset=utf-8"); include 'Classes/PHPExcel.ph ...