[Abp 源码分析]六、工作单元的实现 (4)

代码很简单,调用 Complete()/CompleteAsync() 会将 _isCompleteCalled 置为 true,然后在 Dispose() 方法内会进行检测,为 faslse 的话直接抛出异常。可以看到在 InnerUnitOfWorkCompleteHandle 内部并不会真正地调用 DbContext.SaveChanges() 进行数据保存。

那么谁才是真正进行数据库操作的工作单元呢?

答案就是之前在 IUnitOfWorkManager.Begin() 里面,可以看到在创建 UOW 对象的时候,他在内部进行了一个判断,如果不存在外部工作单元的情况下才会创建 InnerUnitOfWorkCompleteHandle 对象,否则是解析的一个 IUnitOfWork 对象。

也就是说你可以想象有以下代码:

public void TestUowMethod() { using(var outerUOW = Manager.Begin()) // 这里返回的是 IOC 解析出的 IUnitOfWork { OperationOuter(); using(var innerUOW1 = Manager.Begin()) // 内部 UOW { Operation1(); using(var innerUOW2 = Manager.Begin()) // 内部 UOW { Operation2(); Complete(); } Complete(); } Complete(); } }

当代码执行的时候,如同俄罗斯套娃,从内部依次到外部执行,内部工作单元仅会在调用 Complete 方法的时候将 completed 标记为 true,但一旦操作抛出异常,Complete() 无法得到执行,则会直接抛出异常,中断外层代码执行。

在 ABP 内部针对 EF Core 框架实现了一套 UOW,其继承自 UnitOfWorkBase,而在 UnitOfWorkBase 内部有部分针对接口 IActiveUnitOfWork 的实现,同时由于 IUnifOfWork 也实现了 IUnitOfWorkCompleteHandle 接口,所以在 Begin() 方法处能够向上转型。

2.3 抽象工作单元

根据上图可以知道 Abp 默认实现了一个 UnitOfWorkBase 作为工作单元的抽象基类,他主要的属性就是 Id 与 Outer 属性。

public abstract class UnitOfWorkBase : IUnitOfWork { public string Id { get; } [DoNotWire] public IUnitOfWork Outer { get; set; } }

这里的 Id 是使用的 Guid 生成的,用于标识每个工作单元。

而 Outer 则是当前 UOW 对象的引用对象。

这里重点说一下 Outer 是哪儿来的,Outer 他的值是在之前的 UnitOfWorkManager.Begin() 里面的 _currentUnitOfWorkProvider.Current = uow; 进行设置的,_currentUnitOfWorkProvider 的实现在 AsyncLocalCurrentUnitOfWorkProvider 内部,其作用是维护一个 UOW 链,确保当前的工作单元始终是最新的,这里的代码原本是使用 CallContext 实现的,现在已经换为 AsyncLocal<T> 了。

public class AsyncLocalCurrentUnitOfWorkProvider : ICurrentUnitOfWorkProvider, ITransientDependency { /// <inheritdoc /> [DoNotWire] public IUnitOfWork Current { get { return GetCurrentUow(); } set { SetCurrentUow(value); } } public ILogger Logger { get; set; } private static readonly AsyncLocal<LocalUowWrapper> AsyncLocalUow = new AsyncLocal<LocalUowWrapper>(); public AsyncLocalCurrentUnitOfWorkProvider() { Logger = NullLogger.Instance; } private static IUnitOfWork GetCurrentUow() { var uow = AsyncLocalUow.Value?.UnitOfWork; if (uow == null) { return null; } if (uow.IsDisposed) { AsyncLocalUow.Value = null; return null; } return uow; } private static void SetCurrentUow(IUnitOfWork value) { lock (AsyncLocalUow) { if (value == null) { if (AsyncLocalUow.Value == null) { return; } if (AsyncLocalUow.Value.UnitOfWork?.Outer == null) { AsyncLocalUow.Value.UnitOfWork = null; AsyncLocalUow.Value = null; return; } AsyncLocalUow.Value.UnitOfWork = AsyncLocalUow.Value.UnitOfWork.Outer; } else { if (AsyncLocalUow.Value?.UnitOfWork == null) { if (AsyncLocalUow.Value != null) { AsyncLocalUow.Value.UnitOfWork = value; } AsyncLocalUow.Value = new LocalUowWrapper(value); return; } value.Outer = AsyncLocalUow.Value.UnitOfWork; AsyncLocalUow.Value.UnitOfWork = value; } } } private class LocalUowWrapper { public IUnitOfWork UnitOfWork { get; set; } public LocalUowWrapper(IUnitOfWork unitOfWork) { UnitOfWork = unitOfWork; } } }

继续往下看,在 UnitOfWorkBase 的里面也是有个 Complete() 与 CompleteAsync() 方法的。

protected abstract void CompleteUow(); /// <inheritdoc/> public void Complete() { // 判断是否重复完成 PreventMultipleComplete(); try { CompleteUow(); _succeed = true; OnCompleted(); } catch (Exception ex) { _exception = ex; throw; } }

这里的 CompleteUow() 仍然只是一个抽象方法,具体的实现在具体的访问层里面。

2.4 EF Core 实际处理

Abp 针对 EF Core 的 UOW 实现是 EfCoreUnitOfWork,代码如下:

protected override void BeginUow() { if (Options.IsTransactional == true) { _transactionStrategy.InitOptions(Options); } } public override void SaveChanges() { foreach (var dbContext in GetAllActiveDbContexts()) { SaveChangesInDbContext(dbContext); } } public override async Task SaveChangesAsync() { // 遍历所有激活的 DbContext foreach (var dbContext in GetAllActiveDbContexts()) { await SaveChangesInDbContextAsync(dbContext); } } protected override void CompleteUow() { SaveChanges(); CommitTransaction(); } protected override async Task CompleteUowAsync() { await SaveChangesAsync(); CommitTransaction(); } private void CommitTransaction() { if (Options.IsTransactional == true) { _transactionStrategy.Commit(); } } public IReadOnlyList<DbContext> GetAllActiveDbContexts() { return ActiveDbContexts.Values.ToImmutableList(); }

根本就是遍历 DbContext 调用其 SaveChanges() 来提交所有数据库更改。

余下更加详细的东西会放在 《七、仓储与 Entity Framework Core》 当中说明的。

3.常见问题 3.1 待写

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

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