首先声明:
其实DbContext 的设计是很棒的,对于使用者更***,更开放。
本文分享的关于工作单元的设计和封装是针对我们经常面临的特定的场景下对 DbContext 的取用、生命周期的维护进行的,目的是更优美,更方便的完成对工作单元的使用。
DbContext 的生存期从创建实例时开始,并在释放实例时结束。我们对数据库的操作都需要通过 DbContext 进行实现。简单粗暴的方式是使用是 new 一个 DbContext 对象,操作完再进行 Dispose ,但这样对使用者不友好。考虑再三,我认为可以从解决以下几个问题:
对DbContext 的取用的封装
参考Abp对工作单元的封装:
https://aspnetboilerplate.com/Pages/Documents/Unit-Of-Work
封装的非常巧妙和智能,但是也隐藏了很多细节,存在过度封装的嫌疑,但关于 DbContext 的取用可以视为是其中的精华,有很大的借鉴意义。
在Abp中 DbContext 是存放在 AsyncLocal<T> 类型的静态字段中
此数据结构的详细介绍请阅读《C#并发编程系列》中的《多线程共享变量和 AsyncLocal》
简单来说就是 AsyncLocal<T> 类型的静态字段在被并发访问时其中的一个线程及其辅助线程读取写入共享单对其他线程隔离
对 DbContext 的生命周期维护的封装
根据 DbContext 的初始化对依赖注入的配置,使用范围型的方式依赖注入注册 DbContext
通过范围型(IServiceScope)的服务提供者(ServiceProvider)控制 DbContext 的生命周期。
对 DbContext 的CURD进行封装
采用仓储的方式对 Insert, Update, Get, Delete 等数据访问操作进行封装
3.2. 设计 3.2.1. 类图classDiagram class AsyncLocalCurrentUnitOfWorkHandleProvider~TDbContext~{ -AsyncLocal~LocalUowWrapper~ AsyncLocalUow$ +GetCurrentUow()$ IUnitOfWorkHandle~TDbContext~ +SetCurrentUow(IUnitOfWorkHandle~TDbContext~ value)$ } class LocalUowWrapper~TDbContext~{ + IUnitOfWorkHandle~TDbContext~ UnitOfWorkHandle + LocalUowWrapper~TDbContext~ Outer } class IUnitOfWorkHandle~TDbContext~{ <<Interface>> + GetActiveUnitOfWork() TDbContext + SaveChange() int + SaveChangeAsync() Task<int> + IsDisposed() bool } UnitOfWorkHandle~TDbContext~ InnerUnitOfWorkHandle~TDbContext~ class IUnitOfWorkManager{ <<Interface>> +Begin() IUnitOfWorkHandle~TDbContext~ +BeginNew() IUnitOfWorkHandle~TDbContext~ } AsyncLocalCurrentUnitOfWorkHandleProvider~TDbContext~ ..> LocalUowWrapper~TDbContext~ AsyncLocalCurrentUnitOfWorkHandleProvider~TDbContext~ ..> UnitOfWorkHandle~TDbContext~ UnitOfWorkHandle~TDbContext~ ..|> IUnitOfWorkHandle~TDbContext~ InnerUnitOfWorkHandle~TDbContext~ ..|> IUnitOfWorkHandle~TDbContext~ UnitOfWorkManager ..|> IUnitOfWorkManager UnitOfWorkManager ..> UnitOfWorkHandle~TDbContext~ UnitOfWorkManager ..> InnerUnitOfWorkHandle~TDbContext~ UnitOfWorkManager ..> AsyncLocalCurrentUnitOfWorkHandleProvider~TDbContext~