Entity Framework修改记录时丢失CreatedOn等审计数据的问题
解决ASP.NET Core Web API中EF Core+SQLite修改记录时CreatedBy/CreatedOn丢失的问题
问题原因
当修改实体时,如果CreatedOn、CreatedBy字段在当前被跟踪的实体实例中未保留原有值(比如通过DTO映射实体时未传递这两个字段),EF Core会将这些字段的当前默认值(通常为null)更新到数据库,从而覆盖原有有效值。
解决方案
方案一:在SaveChangesAsync中锁定Created字段的修改状态
修改重写的SaveChangesAsync方法,对处于Modified状态的实体,强制将CreatedOn、CreatedBy字段的修改状态设为false,确保数据库中原有值不会被覆盖:
private readonly IHttpContextAccessor _contextAccessor; public EvaDbContext(DbContextOptions options, IHttpContextAccessor contextAccessor) : base(options) { _contextAccessor = contextAccessor; } public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { if (!_contextAccessor.IsLoginRequeust()) { var entityEntries = ChangeTracker.Entries() .Where(e => e.Entity is DomainObject && (e.State == EntityState.Added || e.State == EntityState.Modified)); var userId = _contextAccessor.GetUserId(); foreach (var entityEntry in entityEntries) { var domainObj = (DomainObject)entityEntry.Entity; if (entityEntry.State == EntityState.Modified) { // 更新修改审计字段 domainObj.ModifiedOn = DateTime.Now; domainObj.ModifiedBy = userId; // 锁定Created字段,禁止修改 entityEntry.Property(nameof(DomainObject.CreatedOn)).IsModified = false; entityEntry.Property(nameof(DomainObject.CreatedBy)).IsModified = false; } else if (entityEntry.State == EntityState.Added) { // 设置新增审计字段 domainObj.CreatedOn = DateTime.Now; domainObj.CreatedBy = userId; domainObj.ModifiedOn = DateTime.Now; domainObj.ModifiedBy = userId; } } } return await base.SaveChangesAsync(cancellationToken); }
方案二:配置实体属性为仅新增时赋值
在OnModelCreating方法中,对CreatedOn、CreatedBy字段进行配置,明确其仅在插入时生效,修改时忽略:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<DomainObject>() .Property(x => x.CreatedOn) .ValueGeneratedOnAdd(); // 仅在插入时处理值,修改时忽略 modelBuilder.Entity<DomainObject>() .Property(x => x.CreatedBy) .ValueGeneratedOnAdd(); // 标记为仅插入时赋值,修改时EF会忽略该字段变化 }
方案三:采用"获取实体+局部更新"的业务逻辑
在业务代码中,避免直接调用Update方法更新整个实体,而是先从数据库查询出目标实体,仅修改需要更新的字段:
public async Task UpdateEntity(int entityId, UpdateEntityDto dto) { var targetEntity = await _dbContext.DomainObjects.FindAsync(entityId); if (targetEntity == null) { throw new ArgumentException("实体不存在"); } // 仅更新需要修改的字段 targetEntity.Title = dto.Title; targetEntity.Content = dto.Content; await _dbContext.SaveChangesAsync(); }
这种方式下,EF Core只会跟踪被修改的字段,CreatedOn、CreatedBy会保持数据库中的原有值,不会被覆盖。
内容的提问来源于stack exchange,提问作者Amir Monfared
相关产品推荐
相关产品推荐

