.NET Core使用Dapper更新数据库时Audit日志新旧值相同问题
问题原因
- 核心原因1:
AuditScope默认的对象读取时机错误
你使用的AuditScope.Create("Role:Create", () => roleEntity)重载,传入的是指向roleEntity实例的引用委托。Audit.NET默认不会在Scope初始化阶段序列化目标对象生成旧值快照,只会在Scope释放(即执行到using块末尾)时才读取委托返回的对象生成审计数据。这种情况下旧值和新值读取的是内存中同一个对象的最终状态,自然完全一致。 - 核心原因2:目标实体在Scope生命周期内没有发生任何实际变更
你通过Dapper调用存储过程完成插入操作后,存储过程返回的自增主键Rol_ID_N只存在于Dapper动态参数中,你并没有将这个数据库生成的新值回写赋值到原始roleEntity实例上,整个执行流程中roleEntity的属性值从未改变,即使Audit正确捕获了初始快照,也识别不到任何数据变更。 - 额外配置坑:部分版本的Audit.NET默认关闭了初始状态序列化开关
如果你没有显式给AuditScope配置SerializeInitialState = true,组件不会自动保存对象的初始状态副本,只会记录最终状态,自然无法生成新旧值对比。
修复方案
- 强制开启AuditScope初始化时的初始状态快照,修改Scope创建代码如下:
// 可选:如果担心组件默认深拷贝逻辑有问题,可以提前手动生成初始值副本 var initialValue = JsonSerializer.Deserialize<RoleEntity>(JsonSerializer.Serialize(roleEntity)); using var scope = AuditScope.Create("Role:Create", () => roleEntity, new AuditScopeOptions { // 关键配置:Scope创建时立刻序列化目标对象生成旧值快照 SerializeInitialState = true, EventCreationPolicy = EventCreationPolicy.InsertOnEnd });
- 数据库操作完成后,将所有数据库生成的字段值回写到原始
roleEntity实例,保证新值和旧值存在可识别的差异:
param.Add("@VAR_MODE_V", DBStoredProcedure.OperationMode.INSERT); param.Add("@Rol_ID_N", roleEntity.Rol_ID_N, dbType: DbType.Int32, direction: ParameterDirection.InputOutput); var result = await this._adlerMDBContext.QueryAsync<AdlerSPResponseMessage>(DBStoredProcedure.Role.SPName, AddUserResolverParam(param), commandType: CommandType.StoredProcedure); var roleId = param.Get<int>("@Rol_ID_N"); // 关键:将数据库生成的主键回写到原实体 roleEntity.Rol_ID_N = roleId; if (result?.FirstOrDefault()?.Status == false) { AdlERMLogError($"{errorPrefix}/{nameof(AddAsync)} Exception - {FormatErrorMessage(result)}", null); } return new AdlerResponse<int,long>() { Code = Convert.ToInt32(result?.FirstOrDefault()?.Code), Status = result?.FirstOrDefault()?.Status, Severity = MessageSeverity((int)result?.FirstOrDefault()?.Code), Data = result?.FirstOrDefault()?.TotalRows ?? 0, DataId = roleId, Messages = GetResponseMessages(result), Rows = result?.FirstOrDefault()?.TotalRows ?? 0 };
- 如果后续做更新操作,必须在执行数据库更新命令之前创建AuditScope,不要在更新执行完成后再创建Scope,否则内存中的实体已经被新值覆盖,无法捕获正确的旧值。
内容的提问来源于stack exchange,提问作者Seena N
相关产品推荐
相关产品推荐

