EF Core 5 HasDefaultValue设置后属性赋值为.Net默认值仍触发数据库默认值问题
解决方案
问题根源
- EF Core 对配置了
HasDefaultValue的属性存在默认约定:当属性值等于对应CLR类型的默认值(int为0、bool为false、引用类型为null)时,EF会判定用户未主动赋值,插入时忽略该属性,触发数据库默认值。 - Oracle 驱动特有行为:默认将C#的
string.Empty自动转换为NULL插入,遇到非空列约束时就会抛出ORA-01400异常。
方案1:修改属性保存行为(全局生效,最推荐)
在OnModelCreating配置默认值时,强制EF无论属性值是否等于CLR默认值,只要有赋值就插入:
// 以NewInt为例,其他属性同理配置 modelBuilder .Entity<TestDef>() .Property(e => e.NewInt) .HasDefaultValue(5) .Metadata.SetBeforeSaveBehavior(Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Save);
配置后,EF会将所有显式赋值的字段加入插入语句,只有完全未赋值的字段才会触发数据库默认值。
方案2:使用哨兵值区分主动赋值与未赋值
修改实体类属性的默认值为业务不会用到的哨兵值,让EF可以准确判断是否需要使用数据库默认值:
[Table("TESTDEF")] public class TestDef { // 其他属性省略 [Required] [Column("NEWINT")] public int NewInt { get; set; } = int.MinValue; // 哨兵值,业务不会用到该值 [Required] [Column("NEWBOOLTRUE")] public bool? NewBoolTrue { get; set; } = null; // 用可空类型作为哨兵,区分未赋值和主动赋值false [Required] [Column("NEWBOOLFALSE")] public bool? NewBoolFalse { get; set; } = null; }
该方案不需要修改EF核心配置,仅通过实体类调整即可解决默认值判定问题。
方案3:解决Oracle空字符串转NULL问题
有两种处理方式可选:
- 连接字符串加配置,关闭空字符串转NULL逻辑:
在Oracle连接字符串中添加参数ConvertEmptyStringToNull=false,驱动就会将string.Empty作为空字符串插入。 - 配置值转换器适配业务逻辑:
modelBuilder .Entity<TestDef>() .Property(e => e.NewString) .HasDefaultValue("def") .HasConversion( v => v, v => v ?? string.Empty );
方案4:临时场景手动标记属性修改
如果仅个别场景需要插入CLR默认值,不需要全局修改配置,可在插入后手动标记属性为已修改:
var test = new TestDef() { OldField = "ef bad test", NewInt = 0, NewBoolTrue = false }; _womaDbContext.Add(test); // 手动标记需要强制插入的字段 _womaDbContext.Entry(test).Property(x => x.NewInt).IsModified = true; _womaDbContext.Entry(test).Property(x => x.NewBoolTrue).IsModified = true; await _womaDbContext.SaveChangesAsync();
内容的提问来源于stack exchange,提问作者ufosnowcat
相关产品推荐
相关产品推荐

