[Abp vNext 源码分析] - 4. 工作单元 (3)

这里的配置信息主要指一个工作单元在执行时的 超时时间是否包含一个事务,以及它的 事务隔离级别(如果是事务性的工作单元的话)。

每个工作单元存储了 IDatabaseApi 与 ITransactionApi 的集合,并提供了访问/存储接口。

提供了两个操作事件 Failed 与 Disposed。

这两个事件分别在工作单元执行失败以及被释放时(调用 Dispose() 方法)触发,开发人员可以挂载这两个事件提供自己的处理逻辑。

工作单元还提供了一个工作单元完成事件组。

用于开发人员在工作单元完成时(调用Complete() 方法)挂载自己的处理事件,因为是 List<Func<Task>> 所以你可以指定多个,它们都会在调用 Complete() 方法之后执行,例如如下代码:

using (var uow = _unitOfWorkManager.Begin()) { uow.OnCompleted(async () => completed = true); uow.OnCompleted(async()=>Console.WriteLine("Hello ABP vNext")); uow.Complete(); }

以上信息是我们查看了 UnitOfWork 的属性与接口能够直接得出的结论,接下来我会根据一个工作单元的生命周期来说明一遍工作单元的实现。

一个工作单元的的构造是通过工作单元管理器实现的(IUnitOfWorkManager),通过它的 Begin() 方法我们会获得一个工作单元,至于这个工作单元是外部工作单元还是内部工作单元,取决于开发人员传入的参数。

public IUnitOfWork Begin(UnitOfWorkOptions options, bool requiresNew = false) { Check.NotNull(options, nameof(options)); // 获得当前的工作单元。 var currentUow = Current; // 如果当前工作单元不为空,并且开发人员明确说明不需要构建新的工作单元时,创建内部工作单元。 if (currentUow != null && !requiresNew) { return new ChildUnitOfWork(currentUow); } // 调用 CreateNewUnitOfWork() 方法创建新的外部工作单元。 var unitOfWork = CreateNewUnitOfWork(); // 使用工作单元配置初始化外部工作单元。 unitOfWork.Initialize(options); return unitOfWork; }

这里需要注意的就是创建新的外部工作单元方法,它这里就使用了 IoC 容器提供的 Scope 生命周期,并且在创建之后会将最外部的工作单元设置为最新创建的工作单元实例。

private IUnitOfWork CreateNewUnitOfWork() { var scope = _serviceProvider.CreateScope(); try { var outerUow = _ambientUnitOfWork.UnitOfWork; var unitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork>(); // 设置当前工作单元的外部工作单元。 unitOfWork.SetOuter(outerUow); // 设置最外层的工作单元。 _ambientUnitOfWork.SetUnitOfWork(unitOfWork); unitOfWork.Disposed += (sender, args) => { _ambientUnitOfWork.SetUnitOfWork(outerUow); scope.Dispose(); }; return unitOfWork; } catch { scope.Dispose(); throw; } }

上述描述可能会有些抽象,结合下面这两幅图可能会帮助你的理解。

[Abp vNext 源码分析] - 4. 工作单元

[Abp vNext 源码分析] - 4. 工作单元

我们可以在任何地方注入 IAmbientUnitOfWork 来获取当前活动的工作单元,关于 IAmbientUnitOfWork 与 IUnitOfWorkAccessor 的默认实现,都是使用的 AmbientUnitOfWork。

在该类型的内部,通过 AsyncLocal<IUnitOfWork> 来确保在不同的 异步上下文切换 过程中,其值是正确且统一的。

构造了一个外部工作单元之后,我们在仓储等地方进行数据库操作。操作完成之后,我们需要调用 Complete() 方法来说明我们的操作已经完成了。如果你没有调用 Complete() 方法,那么工作单元在被释放的时候,就会产生异常,并触发 Failed 事件。

public virtual void Dispose() { if (IsDisposed) { return; } IsDisposed = true; DisposeTransactions(); // 只有调用了 Complete()/CompleteAsync() 方法之后,IsCompleted 的值才为 True。 if (!IsCompleted || _exception != null) { OnFailed(); } OnDisposed(); }

所以,我们在手动使用工作单元管理器构造工作单元的时候,一定要注意调用 Complete() 方法。

既然 Complete() 方法这么重要,它内部究竟做了什么事情呢?下面我们就来看一下。

public virtual void Complete() { // 是否已经进行了回滚操作,如果进行了回滚操作,则不提交工作单元。 if (_isRolledback) { return; } // 防止多次调用 Complete 方法,原理就是看 _isCompleting 或者 IsCompleted 是不是已经为 True 了。 PreventMultipleComplete(); try { _isCompleting = true; SaveChanges(); CommitTransactions(); IsCompleted = true; // 数据储存了,事务提交了,则说明工作单元已经完成了,遍历完成事件集合,依次调用这些方法。 OnCompleted(); } catch (Exception ex) { // 一旦在持久化或者是提交事务时出现了异常,则往上层抛出。 _exception = ex; throw; } } public virtual void SaveChanges() { // 遍历集合,如果对象实现了 ISupportsSavingChanges 则调用相应的方法进行数据持久化。 foreach (var databaseApi in _databaseApis.Values) { (databaseApi as ISupportsSavingChanges)?.SaveChanges(); } } protected virtual void CommitTransactions() { // 遍历事务 API 提供者,调用提交事务方法。 foreach (var transaction in _transactionApis.Values) { transaction.Commit(); } } protected virtual void RollbackAll() { // 回滚操作,还是从集合里面判断是否实现了 ISupportsRollback 接口,来调用具体的实现进行回滚。 foreach (var databaseApi in _databaseApis.Values) { try { (databaseApi as ISupportsRollback)?.Rollback(); } catch { } } foreach (var transactionApi in _transactionApis.Values) { try { (transactionApi as ISupportsRollback)?.Rollback(); } catch { } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdfdx.html