EF6.2自定义审计DbContext未检测修改及主键获取问题
解决EF Core审计功能的两个常见问题
嘿,我来帮你搞定这两个EF Core审计功能的痛点!咱们一个个来拆解:
问题1:集合对象的修改无法被检测到
EF Core的ChangeTracker默认不会自动跟踪集合属性内部实体的属性修改——它只会关注集合本身是否被替换、添加或删除元素。如果你的集合里是实体对象,只是修改了实体的属性,ChangeTracker不会把这些标记为Modified状态,导致审计逻辑漏掉这些变化。
解决方案:手动遍历集合导航属性,检查内部实体状态
在你的RecordAudit方法里,除了处理实体本身的状态,还要遍历每个实体的集合导航属性,检查集合内实体的修改情况。示例代码如下:
private void RecordAudit(int userId) { // 获取所有状态变化的实体 var changedEntries = ChangeTracker.Entries() .Where(e => e.State is EntityState.Added or EntityState.Modified or EntityState.Deleted); foreach (var entry in changedEntries) { // 先处理当前实体的审计逻辑(比如新增/修改/删除记录) // ... // 遍历所有导航属性,检查集合类型的属性 foreach (var navigation in entry.Navigations) { if (navigation.IsCollection) { // 获取集合中的实体 var collectionEntities = navigation.CurrentValue as IEnumerable<object>; if (collectionEntities == null) continue; foreach (var item in collectionEntities) { var itemEntry = ChangeTracker.Entries().FirstOrDefault(e => e.Entity == item); // 如果集合内的实体被修改,记录审计 if (itemEntry != null && itemEntry.State == EntityState.Modified) { // 这里添加集合内实体的审计逻辑 // 比如记录哪个属性被修改,修改前后的值等 // ... } } } } } }
这样就能捕获到集合内部实体的属性修改了。
问题2:新增记录时无法获取主键
如果你的主键是数据库生成的(比如自增ID、Identity列),那么在调用base.SaveChanges()之前,EF Core还没从数据库拿到生成的主键值,所以此时实体的主键属性还是默认值(比如0)。
解决方案:先保存主实体获取主键,再记录审计(用事务保证原子性)
你需要调整SaveChanges的执行顺序:先调用base.SaveChanges()拿到主键,再生成审计记录,最后保存审计信息。同时用事务包裹整个过程,确保主实体和审计记录要么都成功,要么都回滚。
示例代码:
public override int SaveChanges(int userId) { using (var transaction = Database.BeginTransaction()) { try { // 第一步:保存主实体,此时数据库会生成主键并赋值给实体 var saveResult = base.SaveChanges(); // 第二步:现在可以获取新增实体的主键了,执行审计记录逻辑 RecordAudit(userId); // 第三步:保存审计记录 base.SaveChanges(); // 提交事务 transaction.Commit(); return saveResult; } catch (Exception ex) { // 出错回滚所有操作 transaction.Rollback(); throw; // 重新抛出异常让上层处理 } } }
异步版本的实现:
public override async Task<int> SaveChangesAsync(int userId, CancellationToken cancellationToken = default) { using (var transaction = await Database.BeginTransactionAsync(cancellationToken)) { try { var saveResult = await base.SaveChangesAsync(cancellationToken); RecordAudit(userId); await base.SaveChangesAsync(cancellationToken); await transaction.CommitAsync(cancellationToken); return saveResult; } catch (Exception ex) { await transaction.RollbackAsync(cancellationToken); throw; } } }
如果你的主键是客户端生成的(比如Guid),那在新增实体时就能直接赋值主键,不需要等SaveChanges——这种情况只要确保在RecordAudit之前已经给主键赋值即可。
内容的提问来源于stack exchange,提问作者DoomerDGR8
相关产品推荐
相关产品推荐

