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
}
}