You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 7.0 EF Core继承审计字段无法保存问题求助

问题排查与解决方案

核心原因分析

你的审计字段无法写入数据库,大概率是以下两个原因之一:

  • EF Core迁移未正确包含审计字段;
  • 未实现审计字段的自动填充逻辑,导致字段值未被正确设置(或仅使用了默认值)。

步骤1:确认迁移包含审计字段

首先检查你的迁移文件(Migrations文件夹下的.cs文件),查看Up方法中是否存在创建Created、CreatedBy、LastModified、LastModifiedBy列的代码。如果没有:

  1. 删除现有的迁移文件;
  2. 重新生成迁移:
    Add-Migration AddAuditFields
    
  3. 更新数据库:
    Update-Database
    

步骤2:实现审计字段自动填充逻辑

手动给每个实体赋值审计字段容易遗漏,推荐通过重写DbContext的SaveChanges方法或使用EF Core拦截器实现自动填充:

方案1:重写DbContext的SaveChanges方法

修改你的DataContext类,添加以下代码:

public class DataContext : DbContext, IApplicationDbContext
{
    public DbSet<Category> Category => Set<Category>();

    // 保留你原有构造函数配置
    public DataContext(DbContextOptions<DataContext> options) : base(options) { }

    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        UpdateAuditFields();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }

    public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
    {
        UpdateAuditFields();
        return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

    private void UpdateAuditFields()
    {
        // 替换为获取当前登录用户ID的逻辑(比如从HttpContext或中介者上下文获取)
        var currentUserId = 1; 

        var auditableEntries = ChangeTracker.Entries<BaseAuditableEntity>();
        foreach (var entry in auditableEntries)
        {
            if (entry.State == EntityState.Added)
            {
                entry.Entity.Created = DateTime.UtcNow;
                entry.Entity.CreatedBy = currentUserId;
            }
            else if (entry.State == EntityState.Modified)
            {
                entry.Entity.LastModified = DateTime.UtcNow;
                entry.Entity.LastModifiedBy = currentUserId;
                // 禁止修改创建时的审计字段
                entry.Property(x => x.Created).IsModified = false;
                entry.Property(x => x.CreatedBy).IsModified = false;
            }
        }
    }
}

方案2:使用EF Core SaveChangesInterceptor

  1. 创建审计拦截器类:
public class AuditInterceptor : SaveChangesInterceptor
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuditInterceptor(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
    {
        UpdateAuditFields(eventData.Context);
        return base.SavingChanges(eventData, result);
    }

    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
    {
        UpdateAuditFields(eventData.Context);
        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }

    private void UpdateAuditFields(DbContext? context)
    {
        if (context == null) return;

        // 从HttpContext获取当前用户ID,替换为你的实际用户ID获取逻辑
        var userIdClaim = _httpContextAccessor.HttpContext?.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
        var currentUserId = userIdClaim != null ? int.Parse(userIdClaim.Value) : 1;

        var auditableEntries = context.ChangeTracker.Entries<BaseAuditableEntity>();
        foreach (var entry in auditableEntries)
        {
            if (entry.State == EntityState.Added)
            {
                entry.Entity.Created = DateTime.UtcNow;
                entry.Entity.CreatedBy = currentUserId;
            }
            else if (entry.State == EntityState.Modified)
            {
                entry.Entity.LastModified = DateTime.UtcNow;
                entry.Entity.LastModifiedBy = currentUserId;
                entry.Property(x => x.Created).IsModified = false;
                entry.Property(x => x.CreatedBy).IsModified = false;
            }
        }
    }
}
  1. 在Program.cs中注册拦截器:
builder.Services.AddHttpContextAccessor();
builder.Services.AddDbContext<DataContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
           .AddInterceptors(new AuditInterceptor(builder.Services.BuildServiceProvider().GetRequiredService<IHttpContextAccessor>())));

步骤3:种子数据中手动设置审计字段

种子数据执行时没有HttpContext,需要手动赋值审计字段:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Category>().HasData(
        new Category
        {
            Id = 1,
            Name = "示例分类",
            Description = "示例描述",
            Created = DateTime.UtcNow,
            CreatedBy = 1 // 替换为种子数据对应的用户ID
        });
}

内容的提问来源于stack exchange,提问作者Rahul Parab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 21:32:00