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

EF Core首次插入时如何将阴影属性值设为另一属性值?

在EF Core中同步阴影属性值并设置只读的解决方案

一、插入时让CreatedAt阴影属性匹配UpdatedAt的值

你可以通过两种方式实现插入实体时,将CreatedAt阴影属性的值设置为UpdatedAt的值:

方法1:重写DbContext的SaveChanges方法

在你的DbContext中重写SaveChanges和SaveChangesAsync方法,监听实体添加状态,同步属性值:

public class YourDbContext : DbContext
{
    // ... 其他DbContext配置

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

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

    private void SyncCreatedAtOnAdd()
    {
        foreach (var entry in ChangeTracker.Entries<Notification>()
            .Where(e => e.State == EntityState.Added))
        {
            var updatedAt = entry.Property<DateTime>(nameof(Notification.UpdatedAt)).CurrentValue;
            entry.Property<DateTime>("CreatedAt").CurrentValue = updatedAt;
        }
    }
}

方法2:使用SaveChangesInterceptor(推荐)

创建拦截器类,统一处理实体插入时的属性同步,这种方式更灵活,便于复用:

public class NotificationShadowPropertyInterceptor : SaveChangesInterceptor
{
    public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
    {
        SyncCreatedAt(eventData.Context);
        return base.SavingChanges(eventData, result);
    }

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

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

        foreach (var entry in context.ChangeTracker.Entries<Notification>()
            .Where(e => e.State == EntityState.Added))
        {
            var updatedAt = entry.Property<DateTime>(nameof(Notification.UpdatedAt)).CurrentValue;
            entry.Property<DateTime>("CreatedAt").CurrentValue = updatedAt;
        }
    }
}

然后在DbContext的配置中注册拦截器:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.AddInterceptors(new NotificationShadowPropertyInterceptor());
    // ... 其他配置
}

二、将CreatedAt设置为只读,禁止修改

要彻底阻止CreatedAt被修改,需要结合模型配置和拦截器校验:

步骤1:模型配置标记为仅插入生成

在IEntityTypeConfiguration中,将CreatedAt配置为仅在插入时生成:

public void Configure(EntityTypeBuilder<Notification> builder)
{
    // ... 其他属性配置
    builder.Property(p => p.UpdatedAt).HasDefaultValueSql("GETUTCDATE()");

    // 配置CreatedAt阴影属性:仅插入时生成,默认值为UTC时间
    builder.Property<DateTime>("CreatedAt")
        .HasDefaultValueSql("GETUTCDATE()")
        .ValueGeneratedOnAdd();
}

步骤2:拦截器阻止修改

在之前的拦截器中添加校验逻辑,一旦检测到CreatedAt被修改,抛出异常或重置值:

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

    // 插入时同步值
    foreach (var entry in context.ChangeTracker.Entries<Notification>()
        .Where(e => e.State == EntityState.Added))
    {
        var updatedAt = entry.Property<DateTime>(nameof(Notification.UpdatedAt)).CurrentValue;
        entry.Property<DateTime>("CreatedAt").CurrentValue = updatedAt;
    }

    // 阻止修改CreatedAt
    foreach (var entry in context.ChangeTracker.Entries<Notification>()
        .Where(e => e.State == EntityState.Modified))
    {
        var createdAtProp = entry.Property<DateTime>("CreatedAt");
        if (createdAtProp.IsModified)
        {
            // 方案1:抛出异常,明确禁止修改
            throw new InvalidOperationException("CreatedAt属性为只读,不允许修改");
            
            // 方案2:静默重置为原始值
            // createdAtProp.CurrentValue = createdAtProp.OriginalValue;
            // createdAtProp.IsModified = false;
        }
    }
}

这样就能确保CreatedAt在插入时同步UpdatedAt的值,并且后续无法被修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:33:17