Skip to main content

事务操作(Tran / TranAsync)

IChameleonDbContext 的事务方法 Tran / TranAsync(又称"数据库事务 / 原子操作")将多个数据库操作包裹在同一事务中执行,保证原子性:要么全部提交,要么全部回滚。编写 C# 脚本和微服务代码时一律使用异步版本 TranAsync。

概述​

  • 事务范围以传入的 lambda 为界,lambda 内通过同一个 IChameleonDbContext 实例执行的所有写操作都在该事务中
  • 支持嵌套:事务内再调用 Tran / TranAsync 不会开启新事务,而是引用计数加一;只有最外层 lambda 正常结束时才提交,任何一层抛异常且传播到最外层时回滚
  • 事务内的写操作产生的数据变更审计日志会延迟到提交前统一写出;回滚时审计日志一并丢弃
  • 事务中调用 GetDatabase() 返回的是事务绑定的连接(UnitOfWork.Orm),保证同一连接执行
  • 实例被释放(Dispose)时若仍有未完成事务,会强制回滚
  • 可用 InTransaction 属性判断当前是否处于事务中

API 方法​

// 异步版本(推荐)
Task TranAsync(Func<Task> func);
Task<TResult> TranAsync<TResult>(Func<Task<TResult>> func);

// 同步版本
void Tran(Action func);
TResult Tran<TResult>(Func<TResult> func);

// 是否处于事务中
bool InTransaction { get; }
参数说明
func事务体委托;内部通过闭包访问同一个 dbContext 执行写操作
TResult带返回值版本,lambda 的返回值即方法返回值

使用示例​

基本事务(订单 + 库存同成功同失败)​

var db = sdk.DbContext;

await db.TranAsync(async () =>
{
await db.AddAsync("Order", orderData);
await db.UpdateAsync("Inventory", inventoryData);
// 两者在同一事务:任一失败则全部回滚
});

带返回值的事务​

var result = await db.TranAsync(async () =>
{
await db.AddAsync("Order", orderData);
await db.AddAsync("OrderItem", items);
return new { ok = true };
});

嵌套事务​

await db.TranAsync(async () =>
{
await db.AddAsync("Order", orderData);

// 内层不会开启新事务;内层异常会传播导致整体回滚
await db.TranAsync(async () =>
{
await db.AddAsync("OrderItem", items);
});
});

事务内抛异常回滚​

await db.TranAsync(async () =>
{
await db.AddAsync("Order", orderData);

if (!stockEnough)
{
throw new Exception("库存不足"); // 触发回滚,Order 不会写入
}

await db.UpdateAsync("Inventory", inventoryData);
});

行为细节​

  • 嵌套通过引用计数实现:每进入一层加一,正常退出减一,减到 0 时提交;异常时减到 0 才回滚并向上抛出原始异常
  • 提交前会先刷出事务期间累积的数据变更审计日志,因此回滚不会产生审计记录
  • 不要在事务 lambda 内使用 Task.Run / 新开线程执行写操作——事务绑定当前上下文与连接,跨线程操作不在事务内
  • 事务中避免执行长时间的外部调用(HTTP、消息队列等),占用数据库连接与锁

下一步​

相关实现​

  • 事务实现:chameleon-core/components/Chameleon.Database/DbContext/ChameleonDbContext.cs(基于 FreeSql UnitOfWork,引用计数嵌套)