小伙伴在使用 HasDefaultValueSql 时,对相关的 ValueGeneratedOnAdd 也有了疑问:
ValueGeneratedOnAdd
和 HasDefaultValueSql
是 Entity Framework Core 中用于管理字段默认值的两种不同配置方式,它们的作用和使用场景不同:
1. ValueGeneratedOnAdd
的作用
ValueGeneratedOnAdd
是一种 值生成策略,表示该字段的值将在数据库插入新记录时自动生成(由数据库负责)。
核心特点
- 自动生成值:EF Core 会明确标记此字段的值应由数据库生成。
- 适用场景:
- 数据库自动生成的默认值(如默认数值、当前时间戳等)。
- 数据库的计算列(
Computed
)。 - 使用主键自增的列(如
IDENTITY
或SEQUENCE
)。
行为
- 如果字段未显式赋值,
ValueGeneratedOnAdd
会 省略该字段,让数据库自动生成其值。 - 即使字段显式赋值,EF Core 也会将其标记为“由数据库生成”,导致插入操作可能报错或忽略应用程序的赋值。
示例代码
模型定义:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Example>()
.Property(e => e.Age)
.ValueGeneratedOnAdd();
}
插入代码:
var example = new Example
{
Name = "Alice" // Age 未赋值
};
context.Examples.Add(example);
context.SaveChanges();
生成的 SQL:
INSERT INTO Example (Name) VALUES ('Alice'); -- Age 列未包含
最终数据库会使用 DEFAULT
值或其他自动生成逻辑。
2. HasDefaultValueSql
的作用
HasDefaultValueSql
用于为数据库字段配置一个 默认值,该默认值由数据库在未显式指定值时自动填充。
核心特点
- 设置字段的默认值:用于在迁移中生成 SQL,如
DEFAULT
或其他基于 SQL 的默认值。 - 适用场景:
- 为字段配置一个静态或动态的默认值。
- 只在 未显式赋值时 使用该默认值。
行为
- 如果字段未显式赋值,EF Core 省略该字段,依赖数据库的
DEFAULT
值。 - 如果字段显式赋值,EF Core 会将显式赋值的值写入
INSERT
语句,覆盖数据库的默认值。
示例代码
模型定义:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Example>()
.Property(e => e.Age)
.HasDefaultValueSql("18");
}
插入代码:
var example = new Example
{
Name = "Bob",
Age = 25 // 显式赋值
};
context.Examples.Add(example);
context.SaveChanges();
生成的 SQL:
INSERT INTO Example (Name, Age) VALUES ('Bob', 25); -- 显式指定值
区别对比
特性 | ValueGeneratedOnAdd | HasDefaultValueSql |
---|---|---|
作用 | 标记值由数据库自动生成。 | 配置字段的默认值(用于迁移和数据库)。 |
插入时行为 | 总是省略未显式赋值的字段,依赖数据库生成值。 | 如果字段未赋值,则省略字段,使用默认值。 |
显式赋值时行为 | 即使显式赋值,可能仍被忽略或导致插入冲突。 | 显式赋值时,生成的 SQL 使用显式值。 |
适用场景 | 自增主键、数据库计算列等需要数据库生成值的字段。 | 静态或动态的默认值,或需要数据库处理时的值。 |
迁移生成行为 | 不设置默认值。 | 生成 DEFAULT 或自定义 SQL 语句。 |
如何选择?
- 如果字段的值 完全交由数据库生成(如自增、计算列等),使用
ValueGeneratedOnAdd
。 - 如果字段有一个 静态或动态的默认值,并且你希望 EF Core 配置该默认值(如
DEFAULT
定义),使用HasDefaultValueSql
。 - 如果你需要两者结合(如自增且有默认值),可以同时使用,但需注意逻辑是否冲突。