- Notifications
You must be signed in to change notification settings - Fork 889
DI UnitOfWorkManager
2881099 edited this page May 15, 2025 · 14 revisions
本篇文章内容引导,如何在 asp.net core 项目中使用特性(注解) 的方式管理事务。
UnitOfWorkManager 只可以管理 Repository 仓储对象的事务
支持六种传播方式(propagation),意味着跨方法的事务非常方便,并且支持同步异步:
- Required:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,默认的选择。
- Supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- Mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- NotSupported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- Never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- Nested:以嵌套事务方式执行。
dotnet add package Rougamo.Fody
[AttributeUsage(AttributeTargets.Method)] public class TransactionalAttribute : Rougamo.MoAttribute { public Propagation Propagation { get; set; } = Propagation.Required; public IsolationLevel IsolationLevel { get => m_IsolationLevel.Value; set => m_IsolationLevel = value; } IsolationLevel? m_IsolationLevel; static AsyncLocal<IServiceProvider> m_ServiceProvider = new AsyncLocal<IServiceProvider>(); public static void SetServiceProvider(IServiceProvider serviceProvider) => m_ServiceProvider.Value = serviceProvider; IUnitOfWork _uow; public override void OnEntry(MethodContext context) { var uowManager = m_ServiceProvider.Value.GetService<UnitOfWorkManager>(); _uow = uowManager.Begin(this.Propagation, this.m_IsolationLevel); } public override void OnExit(MethodContext context) { if (typeof(Task).IsAssignableFrom(context.RealReturnType)) ((Task)context.ReturnValue).ContinueWith(t => _OnExit()); else _OnExit(); void _OnExit() { try { if (context.Exception == null) _uow.Commit(); else _uow.Rollback(); } finally { _uow.Dispose(); } } } }
UnitOfWorkManager 成员 | 说明 |
---|---|
IUnitOfWork Current | 返回当前的工作单元 |
void Binding(repository) | 将仓储的事务交给它管理 |
IUnitOfWork Begin(propagation, isolationLevel) | 创建工作单元 |
//Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddFreeRepository(typeof(Startup).Assembly); services.AddScoped<IFreeSql>(r => r.GetService<UnitOfWorkManager>().Orm); services.AddScoped<UnitOfWorkManager>(r => new UnitOfWorkManager(fsql)); //以这种方式注入的 IFreeSql 会跟随 UowManager 切换事务,原始 fsql 对象为 static 单例 //批量注入 Service } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Use(async (context, next) => { TransactionalAttribute.SetServiceProvider(context.RequestServices); await next(); }); }
public class SongService { readonly IBaseRepository<Song> _repoSong; readonly IBaseRepository<Detail> _repoDetail; readonly SongRepository _repoSong2; public SongService(IBaseRepository<Song> repoSong, IBaseRepository<Detail> repoDetail, SongRepository repoSong2) { _repoSong = repoSong; _repoDetail = repoDetail; _repoSong2 = repoSong2; } [Transactional] public virtual void Test1() { //这里 _repoSong、_repoDetail、_repoSong2 所有操作都是一个工作单元 this.Test2(); } [Transactional(Propagation = Propagation.Nested)] public virtual void Test2() //嵌套事务,新的(不使用 Test1 的事务) { //这里 _repoSong、_repoDetail、_repoSong2 所有操作都是一个工作单元 } }
是不是进方法就开事务呢?
不一定是真实事务,有可能是虚的,就是一个假的 unitofwork(不带事务)
也有可能是延用上一次的事务
也有可能是新开事务,具体要看传播模式
以上使用的是泛型仓储,那我们如果是重写一个仓储 如何保持和UnitOfWorkManager
同一个事务呢。 继承现有的BaseRepository<,>
仓储,实现自定义的仓储SongRepository.cs
,
public class SongRepository : BaseRepository<Song, int>, ISongRepository { public SongRepository(UnitOfWorkManager uowManger) : base(uowManger?.Orm) { uowManger?.Binding(this); } public List<Song> GetSongs() { return Select.Page(1, 10).ToList(); } }
其中接口。ISongRepository.cs
public interface ISongRepository : IBaseRepository<Song, int> { List<Song> GetSongs(); }
在 startup.cs 注入此服务
services.AddScoped<ISongRepository, SongRepository>();
注意:如果是多租户分库场景,请直接使用上面的方案,多租户同一请求大部分都只操作一个数据库,只需要提前将 FreeSqlCloud 对象 Change 切换好。
以 DbEnum 为例定义 FreeSqlCloud 对象如下:
public enum DbEnum { db1, db2 } public class FreeSqlCloud : FreeSqlCloud<DbEnum> //DbEnum 换成 string 就是多租户管理 { public FreeSqlCloud() : base(null) { } public FreeSqlCloud(string distributeKey) : base(distributeKey) { } } public static FreeSqlCloud Cloud = new ...
最终呈现的 AOP 事务代码如下:
class UserRepository : RepositoryCloud<User>, IBaseRepository<User> { public UserRepository(UnitOfWorkManagerCloud uowm) : base(DbEnum.db3, uowm) { } } class UserService : IUserService { readonly IBaseRepository<User> m_repo1; readonly BaseRepository<User> m_repo2; readonly UserRepository m_repo3; public UserService(IBaseRepository<User> repo1, BaseRepository<User> repo2, UserRepository repo3) { m_repo1 = repo1; //db1 m_repo2 = repo2; //db1 m_repo3 = repo3; //db3 } [Transactional(DbEnum.db1)] [Transactional(DbEnum.db3)] public void Test01() { Console.WriteLine("xxx"); //debugger } }
约定好 IBaseRepository<T> 默认是 db1 的仓储实现,注入如下:
public void ConfigureServices(IServiceCollection services) { services.AddSingleton(Cloud); //注入 FreeSqlCloud services.AddSingleton(provider => Cloud.Use(DbEnum.db1)); //注入 IFreeSql services.AddScoped<UnitOfWorkManagerCloud>(); services.AddScoped(typeof(IBaseRepository<>), typeof(RepositoryCloud<>)); //default: db1 foreach (var repositoryType in typeof(User).Assembly.GetTypes().Where(a => a.IsAbstract == false && typeof(IBaseRepository).IsAssignableFrom(a))) services.AddScoped(repositoryType); }
UnitOfWorkManagerCloud、RepositoryCloud、TransactionalAttribute 是我们需要实现的部分:
class UnitOfWorkManagerCloud { readonly Dictionary<string, UnitOfWorkManager> m_managers = new Dictionary<string, UnitOfWorkManager>(); readonly FreeSqlCloud m_cloud; public UnitOfWorkManagerCloud(FreeSqlCloud cloud) { m_cloud = cloud; } public UnitOfWorkManager GetUnitOfWorkManager(string db) { if (m_managers.TryGetValue(db, out var uowm) == false) m_managers.Add(db, uowm = new UnitOfWorkManager(m_cloud.Use(db))); return uowm; } public void Dispose() { foreach(var uowm in m_managers.Values) uowm.Dispose(); m_managers.Clear(); } public IUnitOfWork Begin(string db, Propagation propagation = Propagation.Required, IsolationLevel? isolationLevel = null) { return GetUnitOfWorkManager(db).Begin(propagation, isolationLevel); } } class RepositoryCloud<T> : BaseRepository<T, int> where T : class { public RepositoryCloud(UnitOfWorkManagerCloud uomw) : this(DbEnum.db1, uomw) { } //DI public RepositoryCloud(DbEnum db, UnitOfWorkManagerCloud uomw) : this(uomw.GetUnitOfWorkManager(db.ToString())) { } RepositoryCloud(UnitOfWorkManager uomw) : base(uomw.Orm) { uomw.Binding(this); } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class TransactionalAttribute : Rougamo.MoAttribute { public Propagation Propagation { get; set; } = Propagation.Required; public IsolationLevel IsolationLevel { get => m_IsolationLevel.Value; set => m_IsolationLevel = value; } IsolationLevel? m_IsolationLevel; readonly DbEnum m_db; public TransactionalAttribute(DbEnum db) { m_db = db; } static AsyncLocal<IServiceProvider> m_ServiceProvider = new AsyncLocal<IServiceProvider>(); public static void SetServiceProvider(IServiceProvider serviceProvider) => m_ServiceProvider.Value = serviceProvider; IUnitOfWork _uow; public override void OnEntry(MethodContext context) { var uowManager = m_ServiceProvider.Value.GetService<UnitOfWorkManagerCloud>(); _uow = uowManager.Begin(m_db, this.Propagation, this.m_IsolationLevel); } public override void OnExit(MethodContext context) { if (typeof(Task).IsAssignableFrom(context.RealReturnType)) ((Task)context.ReturnValue).ContinueWith(t => _OnExit()); else _OnExit(); void _OnExit() { try { if (context.Exception == null) _uow.Commit(); else _uow.Rollback(); } finally { _uow.Dispose(); } } } }