ABP源碼分析十:Unit Of Work


ABP以AOP的方式實現UnitOfWork功能。通過UnitOfWorkRegistrar將UnitOfWorkInterceptor在某個類被注冊到IOCContainner的時候,一並添加到該類在容器中對應的ComponentModelInterceptors集合中。總結一句話就是,UOW的功能是通過自定義Castle攔截器來實現的。本文主要介紹ABP核心框架中的UnitOfWork的實現,后續會分別介紹ABP其他模塊是如何具體實現IUnitOfWork的

 

如圖,AbpKernelModule調用UnitOfWorkRegister的Initialize方法將UnitOfWorkInterceptor攔截器添加到標注了UnitOfWork特性法的類,以及實現IRepository或IApplicationService的類上。

 

下圖是UnitOfWorkRegister的代碼

 

和通常實現的AOP一樣,ABP定義了UnitOfWork特性,方便業務層為方法注入UOW功能。

UnitOfWorkAttribute:用於標注某個方法位UnitOfWorkAttribute. 通過這個特性,可以指定是否啟用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的操作是在EfUnitOfWorkNhUnitOfWork這樣的之類中實現的)。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方法有沒有被執行的代碼

 

UnitOfWorkManagerbeign方法。這邊可以看出只有一個IUnitOfWork對象會被創建, 而且由於這個對象是通過容器直接resolve的,那么ABP怎么知道該通過resolve得到什么樣的實例呢?是EfUnitOfWork?還是MongoDbUnitOfWork?還是MemoryDbUnitOfWork?還是NhUnitOfWork?答案是ABP不知道,ABP作者假設你只會用其中一個模塊,所以如果你把這四個module都加入到你的項目中,結果是不可預知的。

 

EfUnitOfWork在begin方法中創建.NET事務

 

EfUnitOfWork提交事務

 

 

 

 

CallContextCurrentUnitOfWorkProvider

CallContextCurrentUnitOfWorkProvider的主要功能其實只有一個:通過current返回當前UOW環境下的UOW實例。

一般思路是:將IUnitOfWork對象定義為實例變量或者是類變量。 但是兩者事實上都不可行。

如果定義為類變量,那就會面臨線程安全的問題,解決方式無非加鎖,但會導致並發能力下降,ABPweb框架,因為鎖導致並發能力下降是不能接受的。

如果定義為實例變量,在同一線程其他地方resolve CallContextCurrentUnitOfWorkProvider這個實例的時候都會得到一個新的實例,新的實例下current自然是NULL.

ABP的做法是:線程邏輯上下文+線程安全的Dictinoray容器

線程邏輯上下文用於存儲UOW實例的key, 而線程邏輯上下文對於本線程是全局可訪問的,而同時具有天然的隔離性。這就確保了當前線程的各個地方都可以得到currentUOWkey

線程安全的Dictinoray容器是一個類實例,用於存放UOW的實例,通過UOWkey就可以取到UOW的實例。

 

 

返回ABP源碼分析系列文章目錄

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM