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

使用自定义ValueGenerator时EF Core为何尝试插入空值?

问题原因分析

你遇到的核心问题是:EF Core 对配置为 ValueGeneratedOnAddOrUpdate 的属性,默认认为该值由数据库生成,因此在 INSERT 语句中不会包含该列;但你的数据库列 LastModified 是 NOT NULL,且你期望通过客户端的 ValueGenerator 生成值,这就导致插入时因 NULL 值触发异常。即使你在 GenerateOnUpdate 中手动设置了值,EF 仍未将该列加入 INSERT 语句,因为它没有检测到该属性需要被包含在插入操作中。


解决方案

1. 调整 GenerateOnUpdate 方法,强制标记属性为已修改

在设置属性值后,显式标记属性为已修改,确保 EF 将其包含在 INSERT 或 UPDATE 语句中:

private void GenerateOnUpdate() { 
    foreach (var entityEntry in ChangeTracker.Entries()) { 
        foreach (var propertyEntry in entityEntry.Properties) { 
            var property = propertyEntry.Metadata; 
            var valueGeneratorFactory = property.GetValueGeneratorFactory(); 
            // 检查属性是否配置为 AddOrUpdate 生成
            var isGeneratedOnAddOrUpdate = (property.ValueGenerated & ValueGenerated.OnAddOrUpdate) != 0;
            if (!isGeneratedOnAddOrUpdate || valueGeneratorFactory == null) { 
                continue; 
            } 

            var valueGenerator = valueGeneratorFactory.Invoke(property, entityEntry.Metadata); 
            var newValue = valueGenerator.Next(entityEntry);
            propertyEntry.CurrentValue = newValue;
            // 标记属性为已修改,强制EF包含该列
            propertyEntry.IsModified = true;
        } 
    } 
}

2. 修复 UtcDateTimeGenerator 的方法实现

确保泛型 Next 方法和非泛型 NextValue 方法逻辑一致,避免EF调用不同方法导致的行为不一致:

public class UtcDateTimeGenerator : ValueGenerator<DateTime> { 
    public override DateTime Next(EntityEntry entry) { 
        return DateTime.UtcNow; 
    } 

    protected override object NextValue(EntityEntry entry) { 
        // 复用泛型方法的逻辑,保持一致性
        return Next(entry); 
    } 

    public override bool GeneratesTemporaryValues => false; 
}

3. 可选:在实体构造函数中初始化默认值

为 AuditedAggregateRoot 添加构造函数,初始化 Created 和 LastModified 的默认值,确保实体创建时属性不为默认的 0001-01-01,辅助EF正确识别属性状态:

public abstract class AuditedAggregateRoot<TId> : AggregateRoot<TId> where TId : IComparable { 
    public DateTime Created { get; internal set; } 
    public DateTime LastModified { get; internal set; }

    protected AuditedAggregateRoot()
    {
        Created = DateTime.UtcNow;
        LastModified = DateTime.UtcNow;
    }
}

然后修改 Player 的构造函数调用基类构造:

public Player([NotNull]string externalId, [NotNull]string displayName, [NotNull]string email) : base() { 
    ExternalId = externalId; 
    DisplayName = displayName; 
    Email = email; 
}

验证效果

完成以上修改后,再次执行插入操作,EF 生成的 INSERT 语句将包含 LastModified 列,并且使用你的 UtcDateTimeGenerator 生成的值,从而避免 NULL 插入异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 05:29:04