diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 26522f106..8165fd41a 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -680,6 +680,15 @@ 事务隔离级别 + + + 创建工作单元 + + 事务传播方式 + 事务隔离级别 + 取消令牌 + + 事务传播方式 diff --git a/FreeSql.DbContext/UnitOfWork/IUnitOfWork.cs b/FreeSql.DbContext/UnitOfWork/IUnitOfWork.cs index b1527e17b..df96ef6f3 100644 --- a/FreeSql.DbContext/UnitOfWork/IUnitOfWork.cs +++ b/FreeSql.DbContext/UnitOfWork/IUnitOfWork.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; +using System.Threading; +using System.Threading.Tasks; namespace FreeSql { @@ -37,5 +39,13 @@ public interface IUnitOfWork : IDisposable /// 用户自定义的状态数据,便于扩展 /// Dictionary States { get; } + +#if NETCOREAPP3_1_OR_GREATER + Task GetOrBeginTransactionAsync(bool isCreate = true, CancellationToken cancellationToken = default); + + Task CommitAsync(CancellationToken cancellationToken = default); + + Task RollbackAsync(CancellationToken cancellationToken = default); +#endif } } diff --git a/FreeSql.DbContext/UnitOfWork/UnitOfWork.cs b/FreeSql.DbContext/UnitOfWork/UnitOfWork.cs index b2a8d368c..c4a86d8b7 100644 --- a/FreeSql.DbContext/UnitOfWork/UnitOfWork.cs +++ b/FreeSql.DbContext/UnitOfWork/UnitOfWork.cs @@ -6,6 +6,7 @@ using System.Data.Common; using System.Linq; using System.Threading; +using System.Threading.Tasks; namespace FreeSql { @@ -99,9 +100,7 @@ public DbTransaction GetOrBeginTransaction(bool isCreate = true) catch (Exception ex) { _fsql?.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(_tranBefore, "失败", ex)); -#pragma warning disable CA2200 // 再次引发以保留堆栈详细信息 - throw ex; -#pragma warning restore CA2200 // 再次引发以保留堆栈详细信息 + throw; } return _tran; } @@ -125,9 +124,7 @@ public void Commit() { if (isCommited == false) _fsql?.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(_tranBefore, "提交失败", ex)); -#pragma warning disable CA2200 // 再次引发以保留堆栈详细信息 - throw ex; -#pragma warning restore CA2200 // 再次引发以保留堆栈详细信息 + throw; } finally { @@ -151,9 +148,7 @@ public void Rollback() { if (isRollbacked == false) _fsql?.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(_tranBefore, "回滚失败", ex)); -#pragma warning disable CA2200 // 再次引发以保留堆栈详细信息 - throw ex; -#pragma warning restore CA2200 // 再次引发以保留堆栈详细信息 + throw; } finally { @@ -181,5 +176,95 @@ public void Dispose() GC.SuppressFinalize(this); } } + +#if NETCOREAPP3_1_OR_GREATER + public async Task GetOrBeginTransactionAsync(bool isCreate = true, CancellationToken cancellationToken = default) + { + if (_tran != null) return _tran; + if (isCreate == false) return null; + if (!Enable) return null; + if (_conn != null) _fsql.Ado.MasterPool.Return(_conn); + + _tranBefore = new Aop.TraceBeforeEventArgs("BeginTransactionAsync", IsolationLevel); + _fsql?.Aop.TraceBeforeHandler?.Invoke(this, _tranBefore); + try + { + _conn = await _fsql.Ado.MasterPool.GetAsync(); + try + { + _tran = IsolationLevel == null ? + await _conn.Value.BeginTransactionAsync(cancellationToken) : + await _conn.Value.BeginTransactionAsync(IsolationLevel.Value, cancellationToken); + + this.Id = $"{DateTime.Now.ToString("yyyyMMdd_HHmmss")}_{Interlocked.Increment(ref _seed)}"; + DebugBeingUsed.TryAdd(this.Id, this); + } + catch + { + ReturnObject(); + throw; + } + } + catch (Exception ex) + { + _fsql?.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(_tranBefore, "失败", ex)); + throw; + } + return _tran; + } + + public async Task CommitAsync(CancellationToken cancellationToken = default) + { + var isCommited = false; + try + { + if (_tran != null) + { + if (_tran.Connection != null) await _tran.CommitAsync(cancellationToken); + isCommited = true; + _fsql?.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(_tranBefore, "提交", null)); + + if (EntityChangeReport != null && EntityChangeReport.OnChange != null && EntityChangeReport.Report.Any() == true) + EntityChangeReport.OnChange.Invoke(EntityChangeReport.Report); + } + } + catch (Exception ex) + { + if (isCommited == false) + _fsql?.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(_tranBefore, "提交失败", ex)); + throw; + } + finally + { + ReturnObject(); + _tranBefore = null; + } + } + + public async Task RollbackAsync(CancellationToken cancellationToken = default) + { + var isRollbacked = false; + try + { + if (_tran != null) + { + if (_tran.Connection != null) await _tran.RollbackAsync(cancellationToken); + isRollbacked = true; + _fsql?.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(_tranBefore, "回滚", null)); + } + } + catch (Exception ex) + { + if (isRollbacked == false) + _fsql?.Aop.TraceAfterHandler?.Invoke(this, new Aop.TraceAfterEventArgs(_tranBefore, "回滚失败", ex)); + throw; + } + finally + { + ReturnObject(); + _tranBefore = null; + } + } +#endif } } diff --git a/FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs b/FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs index 375a0e4fd..7c5700b74 100644 --- a/FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs +++ b/FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs @@ -5,6 +5,7 @@ using System.Data.Common; using System.Linq; using System.Threading; +using System.Threading.Tasks; namespace FreeSql { @@ -137,7 +138,7 @@ IUnitOfWork CreateUowNothing(bool isNotSupported) } IUnitOfWork CreateUow(IsolationLevel? isolationLevel) { - var uow = new UnitOfWorkOrginal(new UnitOfWork(OrmOriginal)); + var uow = new UnitOfWorkOriginal(new UnitOfWork(OrmOriginal)); var uowInfo = new UowInfo(uow, UowInfo.UowType.Orginal, false); if (isolationLevel != null) uow.IsolationLevel = isolationLevel.Value; try { uow.GetOrBeginTransaction(); } @@ -155,6 +156,74 @@ IUnitOfWork CreateUow(IsolationLevel? isolationLevel) return uow; } +#if NETCOREAPP3_1_OR_GREATER + /// + /// 创建工作单元 + /// + /// 事务传播方式 + /// 事务隔离级别 + /// 取消令牌 + /// + public async Task BeginAsync(Propagation propagation = Propagation.Required, IsolationLevel? isolationLevel = null, CancellationToken cancellationToken = default) + { + switch (propagation) + { + case Propagation.Required: return await FindedUowCreateVirtualAsync(cancellationToken) ?? await CreateUowAsync(isolationLevel, cancellationToken); + case Propagation.Supports: return await FindedUowCreateVirtualAsync(cancellationToken) ?? CreateUowNothing(_allUows.LastOrDefault()?.IsNotSupported ?? false); + case Propagation.Mandatory: return await FindedUowCreateVirtualAsync(cancellationToken) ?? throw new Exception(DbContextStrings.Propagation_Mandatory); + case Propagation.NotSupported: return CreateUowNothing(true); + case Propagation.Never: + var isNotSupported = _allUows.LastOrDefault()?.IsNotSupported ?? false; + if (isNotSupported == false) + { + for (var a = _rawUows.Count - 1; a >= 0; a--) + if (await _rawUows[a].Uow.GetOrBeginTransactionAsync(false, cancellationToken) != null) + throw new Exception(DbContextStrings.Propagation_Never); + } + return CreateUowNothing(isNotSupported); + case Propagation.Nested: return await CreateUowAsync(isolationLevel, cancellationToken); + default: throw new NotImplementedException(); + } + } + + async Task CreateUowAsync(IsolationLevel? isolationLevel, CancellationToken cancellationToken = default) + { + var uow = new UnitOfWorkOriginal(new UnitOfWork(OrmOriginal)); + var uowInfo = new UowInfo(uow, UowInfo.UowType.Orginal, false); + if (isolationLevel != null) uow.IsolationLevel = isolationLevel.Value; + try { await uow.GetOrBeginTransactionAsync(true, cancellationToken); } + catch { uow.Dispose(); throw; } + + uow.OnDispose = () => + { + _rawUows.Remove(uowInfo); + _allUows.Remove(uowInfo); + SetAllRepositoryUow(); + }; + _rawUows.Add(uowInfo); + _allUows.Add(uowInfo); + SetAllRepositoryUow(); + return uow; + } + async Task FindedUowCreateVirtualAsync(CancellationToken cancellationToken = default) + { + var isNotSupported = _allUows.LastOrDefault()?.IsNotSupported ?? false; + if (isNotSupported == false) + { + for (var a = _rawUows.Count - 1; a >= 0; a--) + if (await _rawUows[a].Uow.GetOrBeginTransactionAsync(false, cancellationToken) != null) + { + var uow = new UnitOfWorkVirtual(_rawUows[a].Uow); + var uowInfo = new UowInfo(uow, UowInfo.UowType.Virtual, false); + uow.OnDispose = () => _allUows.Remove(uowInfo); + _allUows.Add(uowInfo); + SetAllRepositoryUow(); + return uow; + } + } + return null; + } +#endif class RepoInfo { public IBaseRepository Repository; @@ -180,11 +249,11 @@ public UowInfo(IUnitOfWork uow, UowType type, bool isNotSupported) this.IsNotSupported = isNotSupported; } } - class UnitOfWorkOrginal : IUnitOfWork + class UnitOfWorkOriginal : IUnitOfWork { IUnitOfWork _baseUow; internal Action OnDispose; - public UnitOfWorkOrginal(IUnitOfWork baseUow) => _baseUow = baseUow; + public UnitOfWorkOriginal(IUnitOfWork baseUow) => _baseUow = baseUow; public IFreeSql Orm => _baseUow.Orm; public IsolationLevel? IsolationLevel { get => _baseUow.IsolationLevel; set => _baseUow.IsolationLevel = value; } public DbContext.EntityChangeReport EntityChangeReport => _baseUow.EntityChangeReport; @@ -198,6 +267,11 @@ public void Dispose() _baseUow.Dispose(); OnDispose?.Invoke(); } +#if NETCOREAPP3_1_OR_GREATER + public Task GetOrBeginTransactionAsync(bool isCreate = true, CancellationToken cancellationToken = default) => _baseUow.GetOrBeginTransactionAsync(isCreate, cancellationToken); + public Task CommitAsync(CancellationToken cancellationToken = default) => _baseUow.CommitAsync(cancellationToken); + public Task RollbackAsync(CancellationToken cancellationToken = default) => _baseUow.RollbackAsync(cancellationToken); +#endif } class UnitOfWorkVirtual : IUnitOfWork { @@ -213,6 +287,12 @@ class UnitOfWorkVirtual : IUnitOfWork public void Commit() { } public void Rollback() => _baseUow.Rollback(); public void Dispose() => OnDispose?.Invoke(); + +#if NETCOREAPP3_1_OR_GREATER + public Task GetOrBeginTransactionAsync(bool isCreate = true, CancellationToken cancellationToken = default) => _baseUow.GetOrBeginTransactionAsync(isCreate, cancellationToken); + public Task CommitAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; + public Task RollbackAsync(CancellationToken cancellationToken = default) => _baseUow.RollbackAsync(cancellationToken); +#endif } class UnitOfWorkNothing : IUnitOfWork { @@ -227,12 +307,28 @@ class UnitOfWorkNothing : IUnitOfWork public DbTransaction GetOrBeginTransaction(bool isCreate = true) => null; public void Commit() { - if (EntityChangeReport != null && EntityChangeReport.OnChange != null && EntityChangeReport.Report.Any() == true) + if (EntityChangeReport?.OnChange != null && EntityChangeReport.Report.Any()) EntityChangeReport.OnChange.Invoke(EntityChangeReport.Report); EntityChangeReport?.Report.Clear(); } public void Rollback() => EntityChangeReport?.Report.Clear(); public void Dispose() => OnDispose?.Invoke(); + +#if NETCOREAPP3_1_OR_GREATER + public Task GetOrBeginTransactionAsync(bool isCreate = true, CancellationToken cancellationToken = default) => null; + public Task CommitAsync(CancellationToken cancellationToken = default) + { + if (EntityChangeReport?.OnChange != null && EntityChangeReport.Report.Any()) + EntityChangeReport.OnChange.Invoke(EntityChangeReport.Report); + EntityChangeReport?.Report.Clear(); + return Task.CompletedTask; + } + public Task RollbackAsync(CancellationToken cancellationToken = default) + { + EntityChangeReport?.Report.Clear(); + return Task.CompletedTask; + } +#endif } }