Appearance
事务管理 Transaction Management
目录
事务基础概念
ACID 特性
事务(Transaction) 是数据库操作的原子工作单元,保证数据一致性。
ACID 特性:
A - Atomicity (原子性)
事务中的所有操作要么全部成功,要么全部失败
C - Consistency (一致性)
事务执行前后,数据库保持一致性状态
I - Isolation (隔离性)
并发事务互不干扰
D - Durability (持久性)
事务提交后,变更永久保存EF Core 默认事务行为
csharp
// SaveChanges() 自动使用事务
context.Products.Add(new Product { Name = "Product A", Price = 100 });
context.Products.Add(new Product { Name = "Product B", Price = 200 });
await context.SaveChangesAsync();
// ✅ EF Core 自动开启事务
// ✅ 两个插入在同一个事务中
// ✅ 如果任何一个失败,全部回滚EF Core 事务 API
事务接口层次
csharp
IDbContextTransaction
├─ Commit() / CommitAsync() // 提交事务
├─ Rollback() / RollbackAsync() // 回滚事务
├─ Dispose() // 释放资源
└─ GetDbTransaction() // 获取底层 DbTransaction
IsolationLevel 枚举:
├─ ReadUncommitted // 读未提交(最低隔离)
├─ ReadCommitted // 读已提交(SQL Server 默认)
├─ RepeatableRead // 可重复读
├─ Serializable // 串行化(最高隔离)
└─ Snapshot // 快照隔离显式事务管理
基本用法
csharp
// 方式 1: using 语句(推荐)
using var transaction = await context.Database.BeginTransactionAsync();
try
{
context.Orders.Add(order);
await context.SaveChangesAsync();
orderItems.ForEach(item => context.OrderItems.Add(item));
await context.SaveChangesAsync();
// 所有操作成功,提交事务
await transaction.CommitAsync();
}
catch
{
// 发生异常,回滚事务
await transaction.RollbackAsync();
throw;
}
// 方式 2: try-catch-finally
DbTransaction transaction = null;
try
{
transaction = await context.Database.BeginTransactionAsync();
// 执行操作...
await transaction.CommitAsync();
}
catch
{
transaction?.Rollback();
throw;
}
finally
{
transaction?.Dispose();
}指定隔离级别
csharp
// SQL Server 示例
using var transaction = await context.Database.BeginTransactionAsync(
IsolationLevel.ReadCommitted); // 读已提交
// 其他隔离级别
using var transaction1 = await context.Database.BeginTransactionAsync(
IsolationLevel.Serializable); // 串行化(最强一致性)
using var transaction2 = await context.Database.BeginTransactionAsync(
IsolationLevel.Snapshot); // 快照隔离(高并发)隔离级别对比
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 | 适用场景 |
|---|---|---|---|---|---|
| ReadUncommitted | ✅ 可能 | ✅ 可能 | ✅ 可能 | ⭐⭐⭐⭐⭐ | 统计报表 |
| ReadCommitted | ❌ 不会 | ✅ 可能 | ✅ 可能 | ⭐⭐⭐⭐ | 默认选择 |
| RepeatableRead | ❌ 不会 | ❌ 不会 | ✅ 可能 | ⭐⭐⭐ | 数据一致性要求高 |
| Serializable | ❌ 不会 | ❌ 不会 | ❌ 不会 | ⭐⭐ | 金融交易 |
| Snapshot | ❌ 不会 | ❌ 不会 | ❌ 不会 | ⭐⭐⭐ | 高并发读写 |
高级事务场景
场景 1: 跨 DbContext 事务
csharp
// 多个 DbContext 共享同一连接和事务
var connection = context1.Database.GetDbConnection();
await connection.OpenAsync();
using var transaction = connection.BeginTransaction();
try
{
context1.Database.UseTransaction(transaction);
context2.Database.UseTransaction(transaction);
// 操作 context1
context1.Orders.Add(order);
await context1.SaveChangesAsync();
// 操作 context2
context2.Inventory.Update(stock);
await context2.SaveChangesAsync();
await transaction.Commit();
}
catch
{
await transaction.Rollback();
throw;
}
finally
{
await connection.CloseAsync();
}场景 2: 分布式事务
csharp
// 使用 TransactionScope(需要 MSDTC 服务)
using var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = TimeSpan.FromMinutes(5)
},
TransactionScopeAsyncFlowOption.Enabled); // 支持异步
try
{
// 操作数据库 1
await context1.SaveChangesAsync();
// 操作数据库 2(可能在另一台服务器)
await context2.SaveChangesAsync();
// 调用外部 API
await externalService.ProcessAsync();
// 所有操作成功
scope.Complete(); // 相当于 Commit
}
catch
{
// 自动回滚,无需显式调用
throw;
}场景 3: 保存点(Savepoint)
csharp
// SQL Server 2022+ / PostgreSQL 支持保存点
using var transaction = await context.Database.BeginTransactionAsync();
try
{
// 第一步操作
context.Orders.Add(order);
await context.SaveChangesAsync();
// 创建保存点
await transaction.CreateSavepointAsync("AfterOrder");
try
{
// 第二步操作(可能失败)
context.Payments.Add(payment);
await context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
// 回滚到保存点,保留订单
await transaction.RollbackToSavepointAsync("AfterOrder");
// 记录失败日志,但订单仍然有效
_logger.LogWarning("Payment failed for order {OrderId}", order.Id);
await transaction.CommitAsync(); // 提交订单
}
}
catch
{
await transaction.RollbackAsync();
throw;
}场景 4: 重试策略
csharp
public class ResilientTransaction
{
private readonly AppDbContext _context;
private readonly ILogger _logger;
private const int MaxRetries = 3;
public async Task ExecuteWithRetryAsync(Func<Task> operation)
{
var retryCount = 0;
while (retryCount < MaxRetries)
{
using var transaction = await _context.Database.BeginTransactionAsync();
try
{
await operation();
await transaction.CommitAsync();
_logger.LogInformation("Transaction succeeded on attempt {Attempt}", retryCount + 1);
return; // 成功,退出
}
catch (Exception ex) when (IsTransientError(ex))
{
retryCount++;
if (retryCount >= MaxRetries)
{
_logger.LogError(ex, "Transaction failed after {MaxRetries} attempts", MaxRetries);
throw;
}
_logger.LogWarning(ex, "Transient error, retrying... Attempt {Attempt}/{MaxRetries}",
retryCount, MaxRetries);
// 指数退避等待
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
}
catch
{
await transaction.RollbackAsync();
throw; // 非临时错误,立即抛出
}
}
}
private bool IsTransientError(Exception ex)
{
// 检测临时错误(网络问题、死锁等)
return ex is SqlException sqlEx &&
(sqlEx.Number == 1205 || // 死锁
sqlEx.Number == -2 || // 超时
sqlEx.Number == 64); // 连接中断
}
}
// 使用
var transaction = new ResilientTransaction(context, logger);
await transaction.ExecuteWithRetryAsync(async () =>
{
context.Orders.Add(order);
await context.SaveChangesAsync();
});场景 5: 批量操作事务
csharp
public async Task BulkInsertAsync(List<Product> products)
{
using var transaction = await context.Database.BeginTransactionAsync();
try
{
// 分批插入,每批 1000 条
var batchSize = 1000;
var batches = products.Chunk(batchSize);
foreach (var batch in batches)
{
context.Products.AddRange(batch);
await context.SaveChangesAsync();
// 清理 ChangeTracker,避免内存溢出
context.ChangeTracker.Clear();
}
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}.NET 8/9/10 新特性
.NET 8: 改进的事务性能
csharp
// .NET 8 优化了事务提交性能
// 对于大量小事务,性能提升 15-25%
using var transaction = await context.Database.BeginTransactionAsync();
context.Orders.Add(order);
await context.SaveChangesAsync();
await transaction.CommitAsync(); // 更快!.NET 9: 增强的诊断
csharp
builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(connectionString)
.EnableDetailedErrors()
.LogTo(Console.WriteLine, LogLevel.Information);
});
// .NET 9 输出详细事务信息:
// info: Beginning transaction with isolation level 'ReadCommitted'
// info: Transaction committed successfully in 5ms
// warn: Transaction rolled back due to exception: DbUpdateException.NET 10: 智能事务管理(路线图)
预计特性:
- 自动检测并优化事务范围
- 基于 AI 的死锁预测
- 事务性能分析工具
- 自适应隔离级别选择
最佳实践与陷阱
最佳实践
1. 始终使用 using 语句
csharp
// ✅ 推荐: using 确保资源释放
using var transaction = await context.Database.BeginTransactionAsync();
try
{
// 操作...
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
// 自动 Dispose
// ❌ 避免: 手动管理生命周期
var transaction = await context.Database.BeginTransactionAsync();
// 💥 如果忘记 Dispose,连接泄漏!2. 选择合适的隔离级别
csharp
// ✅ 推荐: 大多数场景使用 ReadCommitted
using var transaction = await context.Database.BeginTransactionAsync(
IsolationLevel.ReadCommitted);
// ✅ 高并发场景使用 Snapshot
using var transaction = await context.Database.BeginTransactionAsync(
IsolationLevel.Snapshot);
// ❌ 避免: 过度使用 Serializable(性能差)
using var transaction = await context.Database.BeginTransactionAsync(
IsolationLevel.Serializable); // 💥 可能导致严重锁竞争3. 保持事务简短
csharp
// ✅ 推荐: 快速提交
using var transaction = await context.Database.BeginTransactionAsync();
context.Orders.Add(order);
await context.SaveChangesAsync();
await transaction.CommitAsync(); // 几毫秒内完成
// ❌ 避免: 长事务
using var transaction = await context.Database.BeginTransactionAsync();
context.Orders.Add(order);
await context.SaveChangesAsync();
// 💥 长时间运行的外部调用,持有锁!
await externalApi.CallAsync(); // 可能需要几秒
await transaction.CommitAsync();4. 处理并发冲突
csharp
using var transaction = await context.Database.BeginTransactionAsync(
IsolationLevel.RepeatableRead); // 防止不可重复读
try
{
var product = await context.Products.FindAsync(id);
// 业务逻辑...
product.Price = newPrice;
await context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch (DbUpdateConcurrencyException)
{
await transaction.RollbackAsync();
// 处理并发冲突
}常见陷阱
陷阱 1: 忘记提交或回滚
csharp
// ❌ 错误: 忘记提交
using var transaction = await context.Database.BeginTransactionAsync();
context.Orders.Add(order);
await context.SaveChangesAsync();
// 💥 忘记 Commit,事务超时后自动回滚!
// ✅ 正确: 明确提交
await transaction.CommitAsync();陷阱 2: 嵌套事务误解
csharp
// ⚠️ 注意: EF Core 不支持真正的嵌套事务
using var outer = await context.Database.BeginTransactionAsync();
using var inner = await context.Database.BeginTransactionAsync();
// 💥 实际上使用的是同一个事务!
// 正确理解: 内层 BeginTransaction 返回的是外层事务的引用陷阱 3: 异常后继续使用连接
csharp
// ❌ 错误: 异常后未回滚就继续
using var transaction = await context.Database.BeginTransactionAsync();
try
{
context.Orders.Add(order);
await context.SaveChangesAsync();
}
catch
{
// 💥 忘记回滚!
}
// 下一个操作会失败
context.Products.Add(product); // 💥 异常!
// ✅ 正确: 异常后立即回滚
catch
{
await transaction.RollbackAsync();
throw;
}陷阱 4: 跨异步上下文丢失事务
csharp
// ❌ 错误: 未启用异步流选项
using (var scope = new TransactionScope())
{
await context.SaveChangesAsync(); // 💥 事务可能丢失
scope.Complete();
}
// ✅ 正确: 启用 AsyncFlowOption
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
TransactionScopeAsyncFlowOption.Enabled)) // ← 关键!
{
await context.SaveChangesAsync(); // ✅ 事务保持
scope.Complete();
}总结
核心要点
- 默认行为: SaveChanges() 自动使用事务
- 显式事务: 跨多个 SaveChanges() 保证原子性
- 隔离级别: ReadCommitted 适合大多数场景
- 资源管理: 始终使用 using 语句
- 异常处理: catch 中必须 Rollback
事务选择指南
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 单个 SaveChanges | 默认事务 | 简单高效 |
| 多个 SaveChanges | 显式事务 | 保证原子性 |
| 跨 DbContext | 共享连接+事务 | 跨上下文一致性 |
| 跨数据库 | TransactionScope | 分布式事务 |
| 高并发 | Snapshot 隔离 | 减少锁竞争 |
| 金融交易 | Serializable | 最强一致性 |
| 批量操作 | 分批+保存点 | 容错能力强 |
代码模板
csharp
// 模板 1: 基本显式事务
using var transaction = await context.Database.BeginTransactionAsync();
try
{
// 操作 1
await context.SaveChangesAsync();
// 操作 2
await context.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
// 模板 2: 指定隔离级别
using var transaction = await context.Database.BeginTransactionAsync(
IsolationLevel.ReadCommitted);
// 模板 3: 重试策略
var retryCount = 0;
while (retryCount < MaxRetries)
{
using var transaction = await context.Database.BeginTransactionAsync();
try
{
// 操作...
await transaction.CommitAsync();
break;
}
catch (Exception ex) when (IsTransientError(ex))
{
retryCount++;
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
}
}
// 模板 4: TransactionScope
using var scope = new TransactionScope(
TransactionScopeOption.Required,
TransactionScopeAsyncFlowOption.Enabled);
// 操作...
scope.Complete();下一步
- 📖 阅读 客户端vs服务端评估
- 🔧 学习 图形更新
- 🚀 了解 自定义迁移操作