跳转到其典型实现 EfCoreTransactionApi 当中,可以看到该类型还实现了 ISupportsRollback 接口。通过这个接口的名字,我们大概就知道它的作用,就是提供了回滚方法的定义。如果某个数据库支持回滚操作,那么就可以为其实现该接口。
其实这里按照语义,你也可以将它放在 EfCoreDatabaseApi<TDbContext> 进行实现,因为回滚也是数据库提供的 API 之一,只是在 ABP vNext 里面又将其归为事务接口进行处理了。
这里就不再详细赘述该类型的具体实现,后续会在单独的 EF Core 章节进行说明。
2.3 工作单元的原理与实现在 ABP vNext 框架当中的工作单元实现,与原来 ABP 框架有一些不一样。
2.3.1 内部工作单元 (子工作单元)首先说内部工作单元的定义,现在是有一个新的 ChildUnitOfWork 类型作为 子工作单元。子工作单元本身并不会产生实际的业务逻辑操作,基本所有逻辑都是调用 UnitOfWork 的方法。
internal class ChildUnitOfWork : IUnitOfWork { public Guid Id => _parent.Id; public IUnitOfWorkOptions Options => _parent.Options; public IUnitOfWork Outer => _parent.Outer; public bool IsReserved => _parent.IsReserved; public bool IsDisposed => _parent.IsDisposed; public bool IsCompleted => _parent.IsCompleted; public string ReservationName => _parent.ReservationName; public event EventHandler<UnitOfWorkFailedEventArgs> Failed; public event EventHandler<UnitOfWorkEventArgs> Disposed; public IServiceProvider ServiceProvider => _parent.ServiceProvider; private readonly IUnitOfWork _parent; // 只有一个带参数的构造函数,传入的就是外部的工作单元(带事务)。 public ChildUnitOfWork([NotNull] IUnitOfWork parent) { Check.NotNull(parent, nameof(parent)); _parent = parent; _parent.Failed += (sender, args) => { Failed.InvokeSafely(sender, args); }; _parent.Disposed += (sender, args) => { Disposed.InvokeSafely(sender, args); }; } // 下面所有 IUnitOfWork 的接口方法,都是调用传入的 UnitOfWork 实例。 public void SetOuter(IUnitOfWork outer) { _parent.SetOuter(outer); } public void Initialize(UnitOfWorkOptions options) { _parent.Initialize(options); } public void Reserve(string reservationName) { _parent.Reserve(reservationName); } public void SaveChanges() { _parent.SaveChanges(); } public Task SaveChangesAsync(CancellationToken cancellationToken = default) { return _parent.SaveChangesAsync(cancellationToken); } public void Complete() { } public Task CompleteAsync(CancellationToken cancellationToken = default) { return Task.CompletedTask; } public void Rollback() { _parent.Rollback(); } public Task RollbackAsync(CancellationToken cancellationToken = default) { return _parent.RollbackAsync(cancellationToken); } public void OnCompleted(Func<Task> handler) { _parent.OnCompleted(handler); } public IDatabaseApi FindDatabaseApi(string key) { return _parent.FindDatabaseApi(key); } public void AddDatabaseApi(string key, IDatabaseApi api) { _parent.AddDatabaseApi(key, api); } public IDatabaseApi GetOrAddDatabaseApi(string key, Func<IDatabaseApi> factory) { return _parent.GetOrAddDatabaseApi(key, factory); } public ITransactionApi FindTransactionApi(string key) { return _parent.FindTransactionApi(key); } public void AddTransactionApi(string key, ITransactionApi api) { _parent.AddTransactionApi(key, api); } public ITransactionApi GetOrAddTransactionApi(string key, Func<ITransactionApi> factory) { return _parent.GetOrAddTransactionApi(key, factory); } public void Dispose() { } public override string ToString() { return $"[UnitOfWork {Id}]"; } }虽然基本上所有方法的实现,都是调用的实际工作单元实例。但是有两个方法 ChildUnitOfWork 是空实现的,那就是 Complete() 和 Dispose() 方法。
这两个方法一旦在内部工作单元调用了,就会导致 事务被提前提交,所以这里是两个空实现。
下面就是上述逻辑的伪代码:
using(var transactioinUow = uowMgr.Begin()) { // 业务逻辑 1 。 using(var childUow1 = uowMgr.Begin()) { // 业务逻辑 2。 using(var childUow2 = uowMgr.Begin()) { // 业务逻辑 3。 childUow2.Complete(); } childUow1.Complete(); } transactioinUow.Complete(); }以上结构一旦某个内部工作单元抛出了异常,到会导致最外层带事务的工作单元无法调用 Complete() 方法,也就能够保证我们的 数据一致性。
2.3.2 外部工作单元首先我们查看 UnitOfWork 类型和 IUnitOfWork 的定义和属性,可以获得以下信息。
每个工作单元是瞬时对象,因为它继承了 ITransientDependency 接口。
每个工作单元都会有一个 Guid 作为其唯一标识信息。
每个工作单元拥有一个 IUnitOfWorkOptions 来说明它的配置信息。