使用自定义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
相关产品推荐
相关产品推荐

