ABP 数据访问 - UnitOfWork 工作单元
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系列也是会继续,欢迎大家来交流;