前情:EFCore HasDefaultValueSql
EFCore HasDefaultValueSql (续1 ValueGeneratedOnAdd)-CSDN博客
小伙伴在使用 HasDefaultValueSql 时,对相关的 ValueGeneratedOnAdd, HasComputedColumnSql 也有了疑问:
HasComputedColumnSql
对于计算列,无论是插入还是编辑,应该由数据库的 SCHEMA
上的计算逻辑负责。对于这种场景,ValueGeneratedOnAdd
并不适合,因为它只针对在插入时生成的值,而计算列可能涉及插入和更新两个阶段的行为。
在 EF Core 中,针对计算列的正确配置应该是使用 ValueGeneratedOnAddOrUpdate
或 HasComputedColumnSql
,以下是详细说明。
1. HasComputedColumnSql
HasComputedColumnSql
是用于配置 计算列 的 Fluent API,告诉 EF Core 该列的值是由数据库根据 SQL 逻辑自动计算的。
核心特点
- 专用于计算列:用于设置数据库的
GENERATED ALWAYS AS
或等效功能。 - 插入和更新行为:EF Core 不会尝试在插入或更新中显式写入该列的值,而是完全依赖数据库的计算逻辑。
- 适用场景:
- 表达式列(如计算两个字段的总和)。
- 基于函数、触发器等逻辑动态生成值的列。
使用示例
假设数据库有一个计算列 TotalPrice
,定义为 Price * Quantity
。
数据库表定义:
CREATE TABLE Orders (
Id INT PRIMARY KEY,
Price DECIMAL(10, 2),
Quantity INT,
TotalPrice AS Price * Quantity PERSISTED
);
EF Core 配置:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.Property(o => o.TotalPrice)
.HasComputedColumnSql("[Price] * [Quantity]", stored: true);
}
解释:
[Price] * [Quantity]
是计算逻辑。stored: true
表示这是一个 持久化计算列(PERSISTED
),结果会存储在数据库中。如果是非持久化列,则省略stored: true
。
插入和更新行为
- 插入时,
INSERT
语句中不会包含TotalPrice
。 - 更新时,
UPDATE
语句中也不会包含TotalPrice
。 - 示例生成的 SQL:
INSERT INTO Orders (Price, Quantity) VALUES (10.00, 2);
2. ValueGeneratedOnAddOrUpdate
ValueGeneratedOnAddOrUpdate
表示字段的值可以在 插入 和 更新 时由数据库生成。这在某些场景下也适用于计算列,但通常需要结合 HasComputedColumnSql
使用。
核心特点
- 同时覆盖 插入 和 更新 的场景。
- 不会显式插入或更新字段,EF Core 会假定该字段的值由数据库生成。
使用场景
如果 EF Core 的 HasComputedColumnSql
不适用(如动态触发器逻辑),可以单独使用 ValueGeneratedOnAddOrUpdate
。
示例配置:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.Property(o => o.TotalPrice)
.ValueGeneratedOnAddOrUpdate();
}
此时,TotalPrice
的计算完全依赖数据库逻辑,EF Core 只会在需要时从数据库中重新加载值。
对比和选择
特性 | HasComputedColumnSql | ValueGeneratedOnAddOrUpdate |
---|---|---|
核心用途 | 显式配置计算列的 SQL 表达式。 | 泛化的生成策略,适用于动态生成值的场景。 |
插入行为 | 不会包含该列,依赖数据库计算。 | 不会包含该列,依赖数据库生成。 |
更新行为 | 不会包含该列,依赖数据库计算。 | 不会包含该列,依赖数据库更新或触发器生成。 |
适用场景 | 数据库定义了明确的计算逻辑(如表达式列)。 | 动态值生成逻辑,如触发器或非表达式列。 |
总结
- 如果字段是严格意义上的 计算列,应使用
HasComputedColumnSql
。 - 如果字段依赖动态触发器逻辑,但没有明确的计算公式,考虑使用
ValueGeneratedOnAddOrUpdate
。 - 通常情况下,
HasComputedColumnSql
是计算列的首选方式,因为它与数据库SCHEMA
定义直接对应,行为明确且易于维护。