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系列也是会继续,欢迎大家来交流;