Audit.Net问题:主记录保存抛出DbUpdateException时审计记录未回滚
你遇到的这个情况核心原因是主数据上下文(DGDBContext)和审计数据上下文(DGAuditDBContext)不在同一个事务范围内。
当你调用SaveChanges时,Audit.EntityFramework默认会先执行主上下文的保存操作,然后再独立提交审计上下文的操作。如果主操作因为数据库约束冲突抛出DbUpdateException,此时主上下文的事务会回滚,但审计上下文的操作已经完成了独立提交,所以审计记录会被保留下来,而主记录却没有保存。
针对这个问题,有两种常见的解决思路,你可以根据自己的架构选择:
方案1:将审计操作与主操作纳入同一个事务(推荐)
如果必须保留独立的审计DbContext,你需要用事务范围来包裹两个上下文的操作,确保任何一方失败时都能一起回滚。
步骤1:安装必要的包
首先确保你的项目引用了System.Transactions包(.NET Core 2.2需要手动安装这个NuGet包)。
步骤2:修改主DbContext的SaveChanges方法
调整SaveChanges和SaveChangesAsync方法,使用TransactionScope来统一事务:
public override int SaveChanges() { using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted })) { try { var result = _helper.SaveChanges(_auditContext, () => base.SaveChanges()); scope.Complete(); return result; } catch { // 异常时事务自动回滚 throw; } } } public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)) { using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) { try { var result = await _helper.SaveChangesAsync(_auditContext, () => base.SaveChangesAsync(cancellationToken)); scope.Complete(); return result; } catch { throw; } } }
这样一来,主上下文和审计上下文的操作都会在同一个事务里,一旦主操作抛出异常,整个事务会回滚,审计记录也不会被插入。
方案2:将审计表移到主DbContext中
如果你的架构允许,可以把审计表整合到主DGDBContext里,这样审计记录的保存会和主记录在同一个DbContext的事务中,异常时自动一起回滚。
步骤1:修改Audit配置
把审计配置中的DbContext改成主上下文:
Audit.Core.Configuration.Setup() .UseEntityFramework(ef => ef .UseDbContext<DGDBContext>() // 使用主上下文替代独立的审计上下文 .AuditTypeExplicitMapper(m => m .Map<AdmissionType, Audit_AdmissionType>() .AuditEntityAction((evt, entry, auditEntity) => { ((dynamic)auditEntity).AuditDate = DateTime.UtcNow; ((dynamic)auditEntity).UserID = user_name; ((dynamic)auditEntity).AuditAction = entry.Action; }) ) );
步骤2:确保主DbContext包含审计表的DbSet
你的DGDBContext已经有Audit_AdmissionType的DbSet,这一步已经满足,无需额外修改。
这样调整后,审计记录会和主记录在同一个SaveChanges调用中提交,当主操作因为约束异常回滚时,审计记录的插入也会被一并回滚。
需要注意的是,Audit.EntityFramework的默认行为是在主操作成功后才保存审计记录,但如果主操作的异常是在数据库层面触发(比如检查约束冲突),主上下文的SaveChanges会直接抛出异常,此时如果审计操作已经提前执行(或者在独立事务中),就会出现不一致的情况。通过上述两种方案,就能确保审计记录和主记录的一致性。
内容的提问来源于stack exchange,提问作者endlesscalm

