EF Core:SaveChangesAsync时CurrentTransaction始终为null问题求助
问题原因分析
你遇到的问题核心在于:EF Core默认的自动事务会在SavedChangesAsync(对应SaveChangesCompletedEventData事件)触发前完成提交/回滚操作,此时事务已被释放,CurrentTransaction自然为null。
解决方案
方案1:在事务未完成的阶段获取ID
将获取事务ID的逻辑移至SavingChangesAsync方法(对应SaveChangesStartingEventData事件),这个阶段EF已创建自动事务且尚未提交,能正常读取CurrentTransaction:
public class AuditSaveChangesInterceptor : ISaveChangesInterceptor { private AuditRecord _record; // 你的审计记录对象 public async ValueTask<int> SavingChangesAsync( SaveChangesStartingEventData eventData, int result, CancellationToken cancellationToken = default) { if (eventData.Context?.Database.CurrentTransaction != null) { _record.TransactionId = eventData.Context.Database.CurrentTransaction.TransactionId; } return result; } // 保留原SavedChangesAsync逻辑(仅处理事务完成后的操作) public async ValueTask<int> SavedChangesAsync( SaveChangesCompletedEventData eventData, int result, CancellationToken cancellationToken = default) { // 这里无法获取TransactionId,仅处理事务完成后的审计逻辑 return result; } }
方案2:通过事务拦截器跟踪ID
如果必须在SavedChangesAsync阶段使用事务ID,可以实现ITransactionInterceptor,在事务创建时记录ID,后续复用:
// 事务拦截器:记录事务ID public class AuditTransactionInterceptor : ITransactionInterceptor { private readonly AuditRecord _auditRecord; public AuditTransactionInterceptor(AuditRecord auditRecord) { _auditRecord = auditRecord; } public void TransactionCreated(TransactionCreatedEventData eventData) { if (eventData.Transaction != null) { _auditRecord.TransactionId = eventData.Transaction.TransactionId; } } // 其他ITransactionInterceptor方法按需实现,无逻辑可留空 public void TransactionCommitted(TransactionEndEventData eventData) { } public void TransactionRolledBack(TransactionEndEventData eventData) { } public Task TransactionCreatedAsync(TransactionCreatedEventData eventData, CancellationToken cancellationToken = default) { TransactionCreated(eventData); return Task.CompletedTask; } } // 审计记录类示例 public class AuditRecord { public Guid? TransactionId { get; set; } // 其他审计字段 }
DI注册时同时添加两个拦截器:
services.AddScoped<AuditRecord>(); services.AddDbContext<CatalogContext>((services, options) => { options.UseSqlServer("<sql-server-connection-string>") .AddInterceptor(services.GetRequiredService<AuditSaveChangesInterceptor>()) .AddInterceptor(services.GetRequiredService<AuditTransactionInterceptor>()); });
补充说明
- 若你是手动开启事务(如
await context.Database.BeginTransactionAsync()),则SavedChangesAsync阶段事务仍存在,CurrentTransaction不会为null,但此场景不适用于当前自动事务的情况。 - 不同数据库的事务ID格式存在差异(SQL Server为Guid,SQLite为整数),但获取方式一致。
内容的提问来源于stack exchange,提问作者Roi Shabtai
相关产品推荐
相关产品推荐

