ABP以AOP的方式实现UnitOfWork功能。通过UnitOfWorkRegistrar将UnitOfWorkInterceptor在某个类被注册到IOCContainner的时候,一并添加到该类在容器中对应的ComponentModel的Interceptors集合中。总结一句话就是,UOW的功能是通过自定义Castle拦截器来实现的。本文主要介绍ABP核心框架中的UnitOfWork的实现,后续会分别介绍ABP其他模块是如何具体实现IUnitOfWork的

如图,AbpKernelModule调用UnitOfWorkRegister的Initialize方法将UnitOfWorkInterceptor拦截器添加到标注了UnitOfWork特性方法的类,以及实现IRepository或IApplicationService的类上。

下图是UnitOfWorkRegister的代码

和通常实现的AOP一样,ABP定义了UnitOfWork特性,方便业务层为方法注入UOW功能。

UnitOfWorkAttribute:用于标注某个方法位UnitOfWork的Attribute类. 通过这个特性,可以指定是否启用UOW,事务隔离级别,TransactionScopeOption等

UnitOfWorkOptions: 封装了UnitOfWork参数的类,其实例是通过UnitOfWorkAttribute的CreateOptions来生成的。


接下来,分析下UnitOfWork是如何封装事务的。

基于接口隔离原则的考量,ABP作者将UnitOfWork的方法分到了三个不同的接口中,如下图。

IUnitOfWorkCompleteHandle:定义了UOW同步和异步的complete方法。实现UOW完成时候的逻辑。

IActiveUnitOfWork:一个UOW除了以上两个接口中定义的方法和属性外,其他的属性和方法都在这个接口定义的。比如Completed,Disposed,Failed事件代理,Filter的enable和disable,以及同步、异步的SaveChanges方法。

IUnitOfWork:继承了上面两个接口。定义了外层的IUnitOfWork的引用和UOW的begin方法。 ABP是通过构建一个UnitOfWork的链,将不同的方法纳入到一个事务中(后文解释)。

UnitOfWorkBase:这个抽象类实现了上面三个接口中定义的方法,而真正实现事务控制的方法是由这个抽象类的子类实现的(比如,真正创建TransactionScope的操作是在EfUnitOfWork,NhUnitOfWork这样的之类中实现的)。UOW中除了事务控制逻辑以外的逻辑都是由UnitOfWorkBase抽象类实现的。

ABP中共有以下4个具体的UOW类型,他们都继承自UnitOfWorkBase。Entity Framework, Nhibernate UnitOfWork是实现事务控制的UOW。MongoDB 和 MemoryDB UnitOfWork是没有事务控制的。 原因很简单,MongoDB本身就没有完整的事务控制功能, 而ABP 框架实现的MemoryDB也是没有事务控制功能的。

IUnitOfWorkManager/UnitOfWorkManager:和其他***manager类一样是一个Facade,他对外提供UOW的功能(用于创建UnitOfWork,并开启UnitOfWork流程),对内调用各种UOW功能的各种组件。

UnitOfWork拦截器调用UnitOfWorkManager开启UOW流程的代码。

Unit Of Work大致运行流程如下

  1. UOW拦截器被注入到需要UOW的类中。
  2. ABP执行标注了UnitOfWork特性的方法时。UOW拦截器以begin()->realAction()->complete()->dispose()顺序执行,其中realAction是被调用的真实业务操作。 UOW拦截器是通过using这种方式调用IUnitOfWork的某个具体实现,这就确保begin 和 dispose也总是会被执行的。 这里需要注意complete却不一定会被执行,比如在complete方法被调用前方法的执行产生了异常。
  3. 当执行一连串的操作时(A方法->B方法->C方法,假设这三个方法都标注了UnitOfWork特性),ABP在执行A方法前会创建整个过程中唯一的IUnitOfWork对象,该对象会启动.NET事务。在执行到B,C方法只会创建InnerUnitOfWorkCompleteHandle。 InnerUnitOfWorkCompleteHandle与IUnitOfWork对象的差异在于它不会创建真实的事务。但ABP会调用其complete,以告知ABP其对应的方法以成功完成,可以提交事务.
  4. 事务可以回滚的关键关键在于IUnitOfWork对象在被dispose时候会检查complete方法有没有被执行,没有的话就认为这个UOW标注的方法没有顺利完成,从而导致事务的回滚操作。
  5. 整个事务的提交是通过第一个UOW(也是唯一个)的complete方法执行时提交的。

InnerUnitOfWorkCompleteHandle关于检查complete方法有没有被执行的代码

UnitOfWorkManager的beign方法。这边可以看出只有一个IUnitOfWork对象会被创建, 而且由于这个对象是通过容器直接resolve的,那么ABP怎么知道该通过resolve得到什么样的实例呢?是EfUnitOfWork?还是MongoDbUnitOfWork?还是MemoryDbUnitOfWork?还是NhUnitOfWork?答案是ABP不知道,ABP作者假设你只会用其中一个模块,所以如果你把这四个module都加入到你的项目中,结果是不可预知的。

EfUnitOfWork在begin方法中创建.NET事务

EfUnitOfWork提交事务

CallContextCurrentUnitOfWorkProvider

CallContextCurrentUnitOfWorkProvider的主要功能其实只有一个:通过current返回当前UOW环境下的UOW实例。

一般思路是:将IUnitOfWork对象定义为实例变量或者是类变量。 但是两者事实上都不可行。

如果定义为类变量,那就会面临线程安全的问题,解决方式无非加锁,但会导致并发能力下降,ABP是web框架,因为锁导致并发能力下降是不能接受的。

如果定义为实例变量,在同一线程其他地方resolve CallContextCurrentUnitOfWorkProvider这个实例的时候都会得到一个新的实例,新的实例下current自然是NULL.

ABP的做法是:线程逻辑上下文+线程安全的Dictinoray容器。

线程逻辑上下文用于存储UOW实例的key, 而线程逻辑上下文对于本线程是全局可访问的,而同时具有天然的隔离性。这就确保了当前线程的各个地方都可以得到current的UOW的key

线程安全的Dictinoray容器是一个类实例,用于存放UOW的实例,通过UOW的key就可以取到UOW的实例。

返回ABP源码分析系列文章目录

ABP源码分析十:Unit Of Work的更多相关文章

  1. C# DateTime的11种构造函数 [Abp 源码分析]十五、自动审计记录 .Net 登陆的时候添加验证码 使用Topshelf开发Windows服务、记录日志 日常杂记——C#验证码 c#_生成图片式验证码 C# 利用SharpZipLib生成压缩包 Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库

    C# DateTime的11种构造函数   别的也不多说没直接贴代码 using System; using System.Collections.Generic; using System.Glob ...

  2. ABP源码分析十二:本地化

    本文逐个分析ABP中涉及到locaization的接口和类,以及相互之间的关系.本地化主要涉及两个方面:一个是语言(Language)的管理,这部分相对简单.另一个是语言对应得本地化资源(Locali ...

  3. ABP源码分析十四:Entity的设计

    IEntity<TPrimaryKey>: 封装了PrimaryKey:Id,这是一个泛型类型 IEntity: 封装了PrimaryKey:Id,这是一个int类型 Entity< ...

  4. ABP源码分析十五:ABP中的实用扩展方法

    类名 扩展的类型 方法名 参数 作用 XmlNodeExtensions XmlNode GetAttributeValueOrNull attributeName Gets an   attribu ...

  5. ABP源码分析十六:DTO的设计

    IDTO:空接口,用于标注Dto对象. ComboboxItemDto:用于combobox/list中Item的DTO NameValueDto<T>/NameValueDto:用于na ...

  6. ABP源码分析十八:UI Inputs

    以下图中描述的接口和类都在Abp项目的Runtime/Validation, UI/Inputs目录下的.在当前版本的ABP(0.83)中这些接口和类并没有实际使用到.阅读代码时可以忽略,无需浪费时间 ...

  7. ABP源码分析十九:Auditing

    审计跟踪(也叫审计日志)是与安全相关的按照时间顺序的记录,它们提供了活动序列的文档证据,这些活动序列可以在任何时间影响一个特定的操作. AuditInfo:定义如下图中需要被Audit的信息. Aud ...

  8. [Abp 源码分析]十二、多租户体系与权限验证

    0.简介 承接上篇文章我们会在这篇文章详细解说一下 Abp 是如何结合 IPermissionChecker 与 IFeatureChecker 来实现一个完整的多租户系统的权限校验的. 1.多租户的 ...

  9. [Abp 源码分析]十六、后台作业与后台工作者

    0. 简介 在某些时候我们可能会需要执行后台任务,或者是执行一些周期性的任务.比如说可能每隔 1 个小时要清除某个临时文件夹内的数据,可能用户会要针对某一个用户群来群发一组短信.前面这些就是典型的应用 ...

随机推荐

  1. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  2. AngularJs之九(ending......)

    今天继续angularJs,但也是最后一篇关于它的了,基础部分差不多也就这些,后续有机会再写它的提升部分. 今天要写的也是一个基础的选择列表: 一:使用ng-options,数组进行循环. <d ...

  3. 构建通用的 React 和 Node 应用

    这是一篇非常优秀的 React 教程,这篇文章对 React 组件.React Router 以及 Node 做了很好的梳理.我是 9 月份读的该文章,当时跟着教程做了一遍,收获很大.但是由于时间原因 ...

  4. [C#] C# 知识回顾 - 学会处理异常

    学会处理异常 你可以使用 try 块来对你觉得可能会出现异常的代码进行分区. 其中,与之关联的 catch 块可用于处理任何异常情况. 一个包含代码的 finally 块,无论 try 块中是否在运行 ...

  5. 非关系型数据库(NoSql)

    最近了解了一点非关系型数据库,刚刚接触,觉得这是一个很好的方向,对于大数据 方面的处理,非关系型数据库能起到至关重要的地位.这里我主要是整理了一些前辈的经验,仅供参考. 关系型数据库的特点 1.关系型 ...

  6. javascript 判断参数类型大全

    js 判断类型的在开发中是很常用的,因为js 是弱类型的语言,var 可以接受任何形式的类型,但是在真正的开发中,我们需要根据不同类型做不同的处理,所以这个是必须的精通. 首先需要知道 typeof这 ...

  7. 关于.NET参数传递方式的思考

    年关将近,整个人已经没有了工作和写作的激情,估计这个时候很多人跟我差不多,该相亲的相亲,该聚会喝酒的聚会喝酒,总之就是没有了干活的心思(我有很多想法,但就是叫不动我的手脚,所以我只能看着别人在做我想做 ...

  8. Asp.net Core准备工作

    1.安装环境 安装.Net Core SDK 安装VS2015 Update3 安装DotNetCore.1.0.1-VS2015Tools.Preview2.0.2.exe 2.新建Core工程 项 ...

  9. Response.Redirect引起的性能问题分析

    现象: 最近做的一个系统通过单点登录(SSO) 技术验证用户登录.用户在SSO 系统上通过验证后,跳转到该系统的不同模块.而跳转的时间一直维持子啊几分钟左右. 分析步骤: 在问题复现时抓取Hang d ...

  10. 极光推送和友盟推送,ios端和安卓端的后端调试设置

    我是最后端的,这两天搞了一个app项目,前端安卓使用友盟很方便,调试比较顺利,然后ios就遇到各种问题了,证书.发送成功推送不成功,测试时用的TestMode(),ios上架之后就必须用product ...