介绍
本次完善ORM仓储层的事务管理,链式调用开启/关闭事务 支持配置数据库隔离级别。
实现
1.异步流隔离
private static readonly AsyncLocal<bool> _withoutTransaction = new AsyncLocal<bool>();
private static readonly AsyncLocal<IsolationLevel?> _isolationLevel = new AsyncLocal<IsolationLevel?>();
AsyncLocal 是线程安全的异步上下文存储,确保每个异步流的事务配置独立,不会相互污染
2.流式API设计
// 链式调用
await _repository.WithoutTransaction().GetListAsync(); // 无事务查询
await _repository.WithIsolationLevel(IsolationLevel.Serializable).AddAsync(entity); // 指定隔离级别
3.统一执行入口
private async Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation)
{
// 读取当前流的配置
bool withoutTran = _withoutTransaction.Value;
IsolationLevel? level = _isolationLevel.Value;
ResetTransactionFlags(); // 用完立即重置,防止污染下一次操作
if (withoutTran)
{
return await operation(); // 裸执行,无事务
}
// 自动开启事务(默认 ReadCommitted)
await _db.Ado.BeginTranAsync(level ?? IsolationLevel.ReadCommitted);
try {
var result = await operation();
await _db.Ado.CommitTranAsync();
return result;
}
catch {
await _db.Ado.RollbackTranAsync();
throw;
}
}
特点
| 特性 | 说明 |
|---|---|
| 零侵入 | 默认自动开启事务,业务代码无感知 |
| 灵活性 | 通过 WithoutTransaction() 跳过事务(适合纯查询) |
| 隔离性 | AsyncLocal 保证并发安全,每个请求独立配置 |
| 防御性 | ResetTransactionFlags() 防止配置残留导致意外行为 |
| 防御编程 | 回滚时用 try-catch 包裹,避免回滚失败时丢失原始异常 |
使用示例
public class UserService
{
private readonly BaseRepository<User, string> _userRepo;
public async Task CreateUserWithOrder(User user, Order order)
{
// 仓储层自动管理事务,Service层专注业务
await _userRepo.AddAsync(user);
// 如果这里抛出异常,事务自动回滚
}
public async Task<List<User>> GetHotUsers()
{
// 纯查询,显式声明不需要事务,性能更好
return await _userRepo.WithoutTransaction()
.GetListAsync();
}
}
完整代码
using System.Linq.Expressions;
using System.Data;
using SqlSugar;
namespace HQ.Common.ORM.SQLSugar;
public class BaseRepository<T, TKey> : IBaseRepository<T, TKey> where T : BaseEntity<TKey>, new()
{
protected ISqlSugarClient _db;
// 异步流隔离 防止并发事务混乱
private static readonly AsyncLocal<bool> _withoutTransaction = new AsyncLocal<bool>();
private static readonly AsyncLocal<IsolationLevel?> _isolationLevel = new AsyncLocal<IsolationLevel?>();
public Func<Task> PreOperationHandler { get; set; }
public BaseRepository(ISqlSugarClient db)
{
_db = db;
}
public BaseRepository<T, TKey> WithoutTransaction()
{
_withoutTransaction.Value = true;
return this;
}
public BaseRepository<T, TKey> WithIsolationLevel(IsolationLevel level)
{
_isolationLevel.Value = level;
return this;
}
private void ResetTransactionFlags()
{
_withoutTransaction.Value = false;
_isolationLevel.Value = null;
}
private async Task ExecutePreHandlerAsync()
{
if (PreOperationHandler != null)
{
await PreOperationHandler.Invoke();
}
}
private async Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> operation)
{
bool withoutTran = _withoutTransaction.Value;
IsolationLevel? level = _isolationLevel.Value;
ResetTransactionFlags();
try
{
await ExecutePreHandlerAsync();
if (withoutTran)
{
return await operation();
}
var isolationLevel = level ?? IsolationLevel.ReadCommitted;
bool isTranStarted = false;
try
{
await _db.Ado.BeginTranAsync(isolationLevel);
isTranStarted = true;
var result = await operation();
await _db.Ado.CommitTranAsync();
return result;
}
catch (Exception)
{
if (isTranStarted)
{
try { await _db.Ado.RollbackTranAsync(); }
catch { }
}
throw;
}
}
catch
{
throw;
}
}
private async Task ExecuteAsync(Func<Task> operation)
{
await ExecuteAsync(async () =>
{
await operation();
return true;
});
}
public async Task<List<T>> GetListAsync()
{
return await ExecuteAsync(async () => await _db.Queryable<T>().ToListAsync());
}
public ISugarQueryable<T> GetAll()
{
ResetTransactionFlags();
return GetQuery();
}
public async Task<bool> DeleteAsync(Expression<Func<T, bool>> whereExpression)
{
return await ExecuteAsync(async () =>
await _db.Deleteable<T>().Where(whereExpression).ExecuteCommandAsync() > 0);
}
public ISugarQueryable<T> GetQuery()
{
ResetTransactionFlags();
return _db.Queryable<T>();
}
public async Task<T> GetByIdAsync(TKey id)
{
return await ExecuteAsync(async () =>
await _db.Queryable<T>().FirstAsync(x => x.Id.Equals(id)));
}
public async Task<bool> AddAsync(T entity)
{
return await ExecuteAsync(async () =>
{
if (typeof(TKey) == typeof(string))
{
var currentId = entity.Id as string;
if (string.IsNullOrWhiteSpace(currentId))
entity.Id = (TKey)(object)Guid.NewGuid().ToString("N");
}
return await _db.Insertable(entity).ExecuteCommandAsync() > 0;
});
}
public async Task<bool> AddRangeAsync(List<T> entities)
{
return await ExecuteAsync(async () =>
await _db.Insertable(entities).ExecuteCommandAsync() > 0);
}
public async Task<bool> UpdateAsync(T entity)
{
return await ExecuteAsync(async () =>
await _db.Updateable(entity).ExecuteCommandAsync() > 0);
}
public async Task<bool> DeleteAsync(TKey id)
{
return await ExecuteAsync(async () =>
await _db.Deleteable<T>().Where(x => x.Id.Equals(id)).ExecuteCommandAsync() > 0);
}
public async Task<bool> DeleteAsync(T entity)
{
return await ExecuteAsync(async () =>
await _db.Deleteable(entity).ExecuteCommandAsync() > 0);
}
public async Task<int> CountAsync()
{
return await ExecuteAsync(async () => await _db.Queryable<T>().CountAsync());
}
public async Task<bool> ExistsAsync(TKey id)
{
return await ExecuteAsync(async () =>
await _db.Queryable<T>().AnyAsync(x => x.Id.Equals(id)));
}
}
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END









暂无评论内容