EF Core中能否实现Update/Delete操作后自动向Proof表插入记录
EF Core中能否实现Update/Delete操作后自动向Proof表插入记录
当然可以实现这种自动插入Proof表的逻辑,不用每次都手动编写插入代码!EF Core提供了几种优雅的方式来处理这类全局的变更追踪与自动操作,下面给你详细说说:
推荐方案:使用SaveChangesInterceptor
这是EF Core 5.0+引入的特性,能全局拦截SaveChanges/SaveChangesAsync操作,非常适合处理这种跨实体的统一记录需求。
步骤1:定义Proof实体
首先确保你已经有了对应的Proof实体类,比如:
public class Proof { public int Id { get; set; } public string TableName { get; set; } public int EntityId { get; set; } public string ColumnName { get; set; } public string OldValue { get; set; } public string NewValue { get; set; } public DateTime ChangeTime { get; set; } public string ChangeType { get; set; } // 标记是"Update"还是"Delete" }
步骤2:实现自定义拦截器
创建一个继承自SaveChangesInterceptor的类,重写SavingChangesAsync(或同步的SavingChanges)方法,在里面捕获变更的实体并生成Proof记录:
public class ProofSaveChangesInterceptor : SaveChangesInterceptor { public override ValueTask<InterceptionResult<int>> SavingChangesAsync( DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default) { var context = eventData.Context; if (context == null) return base.SavingChangesAsync(eventData, result, cancellationToken); // 处理所有被修改或删除的实体 foreach (var entry in context.ChangeTracker.Entries() .Where(e => e.State == EntityState.Modified || e.State == EntityState.Deleted)) { var tableName = entry.Metadata.GetTableName(); var entityId = entry.Property("Id").CurrentValue.ToString(); if (entry.State == EntityState.Modified) { // 遍历所有被修改的列,记录新旧值 foreach (var property in entry.Properties.Where(p => p.IsModified)) { var oldValue = property.OriginalValue?.ToString(); var newValue = property.CurrentValue?.ToString(); context.Add(new Proof { TableName = tableName, EntityId = int.Parse(entityId), ColumnName = property.Name, OldValue = oldValue, NewValue = newValue, ChangeTime = DateTime.Now, ChangeType = "Update" }); } } else if (entry.State == EntityState.Deleted) { // 处理删除操作,记录所有列的原始值 foreach (var property in entry.Properties) { var oldValue = property.OriginalValue?.ToString(); context.Add(new Proof { TableName = tableName, EntityId = int.Parse(entityId), ColumnName = property.Name, OldValue = oldValue, NewValue = null, ChangeTime = DateTime.Now, ChangeType = "Delete" }); } } } return base.SavingChangesAsync(eventData, result, cancellationToken); } }
步骤3:注册拦截器到DbContext
在配置DbContext的时候,把这个拦截器添加进去:
builder.Services.AddDbContext<YourDbContext>(options => options.UseSqlServer("你的连接字符串") .AddInterceptors(new ProofSaveChangesInterceptor()));
这样一来,不管你在代码中对哪个实体执行Update或Delete操作,只要调用SaveChanges/SaveChangesAsync,拦截器就会自动生成对应的Proof记录并插入到数据库中,完全不用手动写插入逻辑!
替代方案:重写DbContext的SaveChanges方法
如果你用的是EF Core 5.0之前的版本,或者更习惯直接修改DbContext,也可以重写SaveChanges/SaveChangesAsync方法,逻辑和上面的拦截器类似:
public class YourDbContext : DbContext { // 这里定义你的DbSet和配置 public override int SaveChanges(bool acceptAllChangesOnSuccess) { GenerateProofRecords(); return base.SaveChanges(acceptAllChangesOnSuccess); } public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) { GenerateProofRecords(); return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); } private void GenerateProofRecords() { // 这里的逻辑和拦截器里的处理几乎一样: // 遍历ChangeTracker中的Modified和Deleted实体,生成Proof记录并添加到上下文 } }
关于手动插入的问题
当然你也可以每次在Update/Delete后手动添加Proof记录,但这种方式不仅繁琐,还容易遗漏(比如忘了在某个业务分支里添加),所以非常不推荐。用上面的全局拦截或重写SaveChanges的方式,能确保所有变更都被统一记录,维护起来也更轻松。
备注:内容来源于stack exchange,提问作者Fishtime
相关产品推荐
相关产品推荐

