You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 18:43:14