ABP 工作單元
ABP中的工作單元是基於AOP實現;采用 Castle組件來完成攔截;
Castle.DynamicProxy
:使用Castle的動態代理來完成方法的攔截
我們首先還是來分析下ABP中工作單元的整個結構圖;
還是先上整體的結構圖
只是描述了類的關聯關系,很多成員並不准確 😄
1.Castle.DynamicProxy 攔截器(UOW注入)
一步步來接開工作單元的面紗;
ABP使用Castle的動態代理來攔截方法,進行AOP注入,再方法之前開啟事務,在方法執行之后統一提交事務;
** Castle.DynamicProxy.IAsyncInterceptor**
該接口定義了標准的攔截器動作,ABP當然也實現了具體的操作
** Abp.Dependency.AbpInterceptorBase**
繼承 IAsyncInterceptor
ABP的基礎連接器,定義了通用的攔截方法,例如異步攔截,同步攔截,以及攔截方法的返回值處理等;
public virtual void InterceptAsynchronous(IInvocation invocation)
{
invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
}
public virtual void InterceptAsynchronous<TResult>(IInvocation invocation)
{
invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
}
public abstract void InterceptSynchronous(IInvocation invocation);
protected abstract Task InternalInterceptAsynchronous(IInvocation invocation);
protected abstract Task<TResult>
InternalInterceptAsynchronous<TResult>(IInvocation invocation);
** UnitOfWorkInterceptor**
繼承 AbpInterceptorBase
定義工作單元的攔截方法,定義在方法執行前以及執行后的相關操作,這里的設計我們可以參考到其他操作中,比如日志處理,我們也可以參考這種設計方法來實現;
話不多說,來分析下具體的攔截過程
protected override async Task InternalInterceptAsynchronous(IInvocation invocation)
{
var proceedInfo = invocation.CaptureProceedInfo();
var method = GetMethodInfo(invocation);
var unitOfWorkAttr = _unitOfWorkOptions.GetUnitOfWorkAttributeOrNull(method);
if (unitOfWorkAttr == null || unitOfWorkAttr.IsDisabled)
{
proceedInfo.Invoke();
var task = (Task)invocation.ReturnValue;
await task.ConfigureAwait(false);
return;
}
using (var uow = _unitOfWorkManager.Begin(unitOfWorkAttr.CreateOptions()))
{
proceedInfo.Invoke();
var task = (Task)invocation.ReturnValue;
await task.ConfigureAwait(false);
await uow.CompleteAsync().ConfigureAwait(false);
}
}
1.做開啟工作單元的判斷
var unitOfWorkAttr = _unitOfWorkOptions.GetUnitOfWorkAttributeOrNull(method);
獲取方法屬性,如果標記了UnitOfWork,則開啟工作單元攔截
2.開啟工作單元
using (var uow = _unitOfWorkManager.Begin(unitOfWorkAttr.CreateOptions()))
{
proceedInfo.Invoke();
var task = (Task)invocation.ReturnValue;
await task.ConfigureAwait(false);
await uow.CompleteAsync().ConfigureAwait(false);
}
這里開啟工作單元,通過unitWorkManager.Begin來開啟,工作單元的提交通過 IUnitOfWorkCompleteHandle 實現;
到此,我們對 UOW 的注入方式已經了解,知道是如何注入到方法中,並進行攔截的。
我們帶着問題進入下一步
unitWorkManager如何控制的事務?
如何進行的事務提交?
以及方法嵌套調用時,如何確保同一事務提交?
2.IUnitOfWork 工作單元實現
IUnitOfWorkCompleteHandle : 定義了UOW同步和異步的complete方法。實現UOW完成時候的邏輯。
IActiveUnitOfWork :一個UOW除了以上兩個接口中定義的方法和屬性外,其他的屬性和方法都在這個接口定義的。比如Completed,Disposed,Failed事件代理,Filter的enable和disable,以及同步、異步的SaveChanges方法。
IUnitOfWork :繼承了上面兩個接口。定義了外層的IUnitOfWork的引用和UOW的begin方法
三個接口來一起完成 UOW 工作;
UnitOfWorkBase : 作為實現上面三個接口的基礎實現,實現一些基礎方法,但是真正的事務操作實現都是各個框架具體的實現,例如:EFUnitOfWork,EfCoreUnitOfWork等不同的實現;
好,接着來,把整體大的結構講完,接着看代碼,上面注入提到會調用UnitOfManager來管理,那我們從它入手,首先分析Begin方法,看下Manager的實現
public IUnitOfWorkCompleteHandle Begin(UnitOfWorkOptions options)
{
options.FillDefaultsForNonProvidedOptions(_defaultOptions);
var outerUow = _currentUnitOfWorkProvider.Current;
if (options.Scope == TransactionScopeOption.Required && outerUow != null)
{
return new InnerUnitOfWorkCompleteHandle();
}
var uow = _iocResolver.Resolve<IUnitOfWork>();
uow.Completed += (sender, args) =>
{
_currentUnitOfWorkProvider.Current = null;
};
uow.Failed += (sender, args) =>
{
_currentUnitOfWorkProvider.Current = null;
};
uow.Disposed += (sender, args) =>
{
_iocResolver.Release(uow);
};
//Inherit filters from outer UOW
if (outerUow != null)
{
options.FillOuterUowFiltersForNonProvidedOptions(outerUow.Filters.ToList());
}
uow.Begin(options);
//Inherit tenant from outer UOW
if (outerUow != null)
{
uow.SetTenantId(outerUow.GetTenantId(), false);
}
_currentUnitOfWorkProvider.Current = uow;
return uow;
}
1.獲取當前環境事務單元
var outerUow = _currentUnitOfWorkProvider.Current;
if (options.Scope == TransactionScopeOption.Required && outerUow != null)
{
return new InnerUnitOfWorkCompleteHandle();
}
獲取當前環境中的事務單元,如果不為空說明是嵌套方法調用,這里通過_currentUnitOfWorkProvider來管理,如果是嵌套方法中的工作單元,則返回一個內部提交操作類,InnerUnitOfWorkCompleteHandle 該類不處理具體事務操作,它的提交只是標識下方法執行狀態
進入Begin方法,如果是第一個方法進來,獲取Current為空,就會重新創建一個Uow,如果是嵌套的方法調用,那會直接返回一個InnerUnitOfWorkCompleteHandle,這里的結構設計還是比較巧妙,解決了工作單元方法嵌套調用,保證存在於一個事務中
public void Complete()
{
_isCompleteCalled = true;
}
2.綁定相關事件,並開啟事務
Begin方法,首先會調用到 UnitOfWorkBase 的Begin方法
public void Begin(UnitOfWorkOptions options)
{
Check.NotNull(options, nameof(options));
PreventMultipleBegin();
Options = options; //TODO: Do not set options like that, instead make a
copy?
SetFilters(options.FilterOverrides);
SetTenantId(AbpSession.TenantId, false);
BeginUow();
}
然后調用BeginUow(),Base類中並沒有具體實現,這里會調用不同實現,這里分析下EfCore實現;
EfCoreUnitOfWork
protected override void BeginUow()
{
if (Options.IsTransactional == true)
{
_transactionStrategy.InitOptions(Options);
}
}
這里會將事務參數傳遞進來,作為EFCore的事務控制,那后續如何和事務關聯呢?如果傳遞的參數是開啟事務模式,后續創建DbContext會以事務模式
這里訪問到 DbContextEfCoreTransactionStrategy
public DbContext CreateDbContext<TDbContext>(string connectionString, IDbContextResolver
dbContextResolver) where TDbContext : DbContext
{
DbContext dbContext;
var activeTransaction = ActiveTransactions.GetOrDefault(connectionString);
if (activeTransaction == null)
{
dbContext = dbContextResolver.Resolve<TDbContext>(connectionString, null);
var dbtransaction =
dbContext.Database.BeginTransaction((Options.IsolationLevel ??
IsolationLevel.ReadUncommitted).ToSystemDataIsolationLevel());
activeTransaction = new ActiveTransactionInfo(dbtransaction, dbContext);
ActiveTransactions[connectionString] = activeTransaction;
}
else
{
dbContext = dbContextResolver.Resolve<TDbContext>(
connectionString, activeTransaction.DbContextTransaction.GetDbTransaction().Connection
);
if (dbContext.HasRelationalTransactionManager())
{
dbContext.Database.UseTransaction(activeTransaction.DbContextTransaction.GetDbTransaction());
}
else
{
dbContext.Database.BeginTransaction();
}
activeTransaction.AttendedDbContexts.Add(dbContext);
}
return dbContext;
}
關注這行代碼
var dbtransaction = dbContext.Database.BeginTransaction((Options.IsolationLevel ?? IsolationLevel.ReadUncommitted).ToSystemDataIsolationLevel());
根據傳入的參數,進行事務的創建
3.工作單元提交
工作單元在完成操作之后,如何進行同一的事務處理呢?還是來分析下EFCore實現
EfCoreUnitOfWork
protected override void CompleteUow()
{
SaveChanges();
CommitTransaction();
}
循環找到所有DbContext,進行保存操作
public override void SaveChanges()
{
foreach (var dbContext in GetAllActiveDbContexts())
{
SaveChangesInDbContext(dbContext);
}
}
private void CommitTransaction()
{
if (Options.IsTransactional == true)
{
_transactionStrategy.Commit();
}
}
調用 DbContextEfCoreTransactionStrategy,進行事務的統一提交
public void Commit()
{
foreach (var activeTransaction in ActiveTransactions.Values)
{
activeTransaction.DbContextTransaction.Commit();
foreach (var dbContext in activeTransaction.AttendedDbContexts)
{
if (dbContext.HasRelationalTransactionManager())
{
continue; //Relational databases use the shared transaction
}
dbContext.Database.CommitTransaction();
}
}
}
至此,UnitOfWork的基本實現就已經分析完了,簡單的對代碼結構進行了整體解讀,但是還是沒有對包含的所有類全部分析完成,但是整體的思路結構都已經分析到了,后面的ABP系列也是會繼續,歡迎大家來交流;