Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ 为unitofwork/manager 增加async 功能 支持 core3.1及以上版本 #1220

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions FreeSql.DbContext/FreeSql.DbContext.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions FreeSql.DbContext/UnitOfWork/IUnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;

namespace FreeSql
{
Expand Down Expand Up @@ -37,5 +39,13 @@ public interface IUnitOfWork : IDisposable
/// 用户自定义的状态数据,便于扩展
/// </summary>
Dictionary<string, object> States { get; }

#if NETCOREAPP3_1_OR_GREATER
Task<DbTransaction> GetOrBeginTransactionAsync(bool isCreate = true, CancellationToken cancellationToken = default);

Task CommitAsync(CancellationToken cancellationToken = default);

Task RollbackAsync(CancellationToken cancellationToken = default);
#endif
}
}
103 changes: 94 additions & 9 deletions FreeSql.DbContext/UnitOfWork/UnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace FreeSql
{
Expand Down Expand Up @@ -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;
}
Expand All @@ -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
{
Expand All @@ -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
{
Expand Down Expand Up @@ -181,5 +176,95 @@ public void Dispose()
GC.SuppressFinalize(this);
}
}

#if NETCOREAPP3_1_OR_GREATER
public async Task<DbTransaction> GetOrBeginTransactionAsync(bool isCreate = true, CancellationToken cancellationToken = default)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public async Task<DbTransaction> GetOrBeginTransactionAsync(bool isCreate = true, CancellationToken cancellationToken = default)
[Obsolete("实验功能,在 AOP 动态代理处理起来会比较麻烦,https://github.com/dotnetcore/FreeSql/pull/1220")]
public async Task<DbTransaction> 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
}
}
104 changes: 100 additions & 4 deletions FreeSql.DbContext/UnitOfWork/UnitOfWorkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Data.Common;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace FreeSql
{
Expand Down Expand Up @@ -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(); }
Expand All @@ -155,6 +156,74 @@ IUnitOfWork CreateUow(IsolationLevel? isolationLevel)
return uow;
}

#if NETCOREAPP3_1_OR_GREATER
/// <summary>
/// 创建工作单元
/// </summary>
/// <param name="propagation">事务传播方式</param>
/// <param name="isolationLevel">事务隔离级别</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
public async Task<IUnitOfWork> 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<IUnitOfWork> 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<IUnitOfWork> 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;
Expand All @@ -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;
Expand All @@ -198,6 +267,11 @@ public void Dispose()
_baseUow.Dispose();
OnDispose?.Invoke();
}
#if NETCOREAPP3_1_OR_GREATER
public Task<DbTransaction> 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
{
Expand All @@ -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<DbTransaction> 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
{
Expand All @@ -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<DbTransaction> 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
}
}

Expand Down