统一工作单元是一个比较重要的基础设施组件,它负责管理整个业务流程当中涉及到的数据库事务,一旦某个环节出现异常自动进行回滚处理。
在 ABP vNext 框架当中,工作单元被独立出来作为一个单独的模块(Volo.Abp.Uow)。你可以根据自己的需要,来决定是否使用统一工作单元。
二、源码分析整个 Volo.Abp.Uow 项目的结构如下,从下图还是可以看到我们的老朋友 IUnitOfWorkManager 和 IUnitOfWork ,不过也多了一些新东西。看一个模块的功能,首先从它的 Module 入手,我们先看一下 AbpUnitofWorkModule 里面的实现。
2.1 工作单元的初始模块打开 AbpUnitOfWorkModule 里面的代码,发现还是有点失望,里面就一个服务注册完成事件。
public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.OnRegistred(UnitOfWorkInterceptorRegistrar.RegisterIfNeeded); }这里的结构和之前看的 审计日志 模块类似,就是注册拦截器的作用,没有其他特别的操作。
2.1.1 拦截器注册继续跟进代码,其实现是通过 UnitOfWorkHelper 来确定哪些类型应该集成 UnitOfWork 组件。
public static void RegisterIfNeeded(IOnServiceRegistredContext context) { // 根据回调传入的 context 绑定的实现类型,判断是否应该为该类型注册 UnitOfWorkInterceptor 拦截器。 if (UnitOfWorkHelper.IsUnitOfWorkType(context.ImplementationType.GetTypeInfo())) { context.Interceptors.TryAdd<UnitOfWorkInterceptor>(); } }继续分析 UnitOfWorkHelper 内部的代码,第一种情况则是实现类型 (implementationType) 或类型的任一方法标注了 UnitOfWork 特性的话,都会为其注册工作单元拦截器。
第二种情况则是 ABP vNext 为我们提供了一个新的 IUnitOfWorkEnabled 标识接口。只要继承了该接口的实现,都会被视为需要工作单元组件,会在系统启动的时候,自动为它绑定拦截器。
public static bool IsUnitOfWorkType(TypeInfo implementationType) { // 第一种方式,即判断具体类型与其方法是否标注了 UnitOfWork 特性。 if (HasUnitOfWorkAttribute(implementationType) || AnyMethodHasUnitOfWorkAttribute(implementationType)) { return true; } // 第二种方式,即判断具体类型是否继承自 IUnitOfWorkEnabled 接口。 if (typeof(IUnitOfWorkEnabled).GetTypeInfo().IsAssignableFrom(implementationType)) { return true; } return false; } 2.2 新的接口与抽象在 ABP vNext 当中,将一些 职责 从原有的工作单元进行了 分离。抽象出了 IDatabaseApi 、ISupportsRollback、ITransactionApi 这三个接口,这三个接口分别提供了不同的功能和职责。
2.2.1 数据库统一访问接口这里以 IDatabaseApi 为例,它是提供了一个 数据库提供者(Database Provider) 的抽象概念,在 ABP vNext 里面,是将 EFCore 作为数据库概念来进行抽象的。(因为后续 MongoDb 与 MemoryDb 与其同级)
你可以看作是 EF Core 的 Provider ,在 EF Core 里面我们可以实现不同的 Provider ,来让 EF Core 支持访问不同的数据库。
而 ABP vNext 这么做的意图就是提供一个统一的数据库访问 API,如何理解呢?这里以 EFCoreDatabaseApi<TDbContext> 为例,你查看它的实现会发现它继承并实现了 ISupportsSavingChanges ,也就是说 EFCoreDatabaseApi<TDbContext> 支持 SaveChanges 操作来持久化数据更新与修改。
public class EfCoreDatabaseApi<TDbContext> : IDatabaseApi, ISupportsSavingChanges where TDbContext : IEfCoreDbContext { public TDbContext DbContext { get; } public EfCoreDatabaseApi(TDbContext dbContext) { DbContext = dbContext; } public Task SaveChangesAsync(CancellationToken cancellationToken = default) { return DbContext.SaveChangesAsync(cancellationToken); } public void SaveChanges() { DbContext.SaveChanges(); } }也就是说 SaveChanges 这个操作,是 EFCore 这个 DatabaseApi 提供了一种特殊操作,是该类型数据库的一种特殊接口。
如果针对于某些特殊的数据库,例如 InfluxDb 等有一些特殊的 Api 操作时,就可以通过一个 DatabaseApi 类型进行处理。
2.2.2 数据库事务接口通过最开始的项目结构会发现一个 ITransactionApi 接口,这个接口只定义了一个 事务提交操作(Commit),并提供了异步方法的定义。
public interface ITransactionApi : IDisposable { void Commit(); Task CommitAsync(); }