实体框架核心:带持久化值的计算列

8

我有点惊讶地发现,关于下面的问题我没有找到任何信息,所以如果我在文档中错过了,请谅解。使用SQL Server(2016本地和Azure)和EFCore Code First,我们尝试创建一个具有持久值的计算表列。创建列可以正常工作,但是我不知道如何保留值。以下是我们的操作:

modelBuilder.Entity<SomeClass>(entity =>
{
    entity.Property(p => p.Checksum)
        .HasComputedColumnSql("(checksum([FirstColumnName], [SecondColumnName]))");
});

以下是我们希望在T-SQL中实际获得的内容:

CREATE TABLE [dbo].[SomeClass]
(
    [FirstColumnName]   [NVARCHAR](10)
  , [SecondColumnName]  [NVARCHAR](10)
  , [Checksum] AS (CHECKSUM([FirstColumnName], [SecondColumnName])) PERSISTED
);

有人能指引我正确的方向吗?

提前感谢您,Tobi

更新:根据@ jeroen-mostert的好主意,我也尝试将PERSISTED字符串作为公式的一部分传递:

modelBuilder.Entity<SomeClass>(entity =>
{
    entity.Property(p => p.Checksum)
        .HasComputedColumnSql("(checksum([FirstColumnName], [SecondColumnName]) PERSISTED)");
});

还有圆括号之外的部分:

modelBuilder.Entity<SomeClass>(entity =>
{
    entity.Property(p => p.Checksum)
        .HasComputedColumnSql("(checksum([FirstColumnName], [SecondColumnName])) PERSISTED");
});

然而出乎意料的是,计算列仍然生成了 Is Persisted = No,因此 PERSISTED 字符串似乎被忽略了。

谢谢您提供的信息。目前我们正在使用该列来检查两个不同表中具有匹配ID值的行之间的差异。根据您的评论,我得出结论最好使用HASHBYTES,因此我会研究一下。然而,原始问题仍然存在,因为我也想保留计算列,无论校验和算法如何。 - Onkel Toob
这听起来一点也不愚蠢,但不幸的是,它似乎不起作用。 - Onkel Toob
“Does not seem to work” 是指什么?它是否出现错误?EF 是否不执行现有列的迁移?(如果将定义视为黑匣子,则更或多或少会出现这种情况。)如果从头开始,它是否将列生成为计算列而不是持久化列?(对我来说,这将是令人惊讶的,因为这意味着 EF 确实解析了定义并费尽心思地忽略了“PERSISTED”。) - Jeroen Mostert
抱歉,我应该更加精确。我已经更新了问题;在公式中添加“PERSISTED”字符串实际上没有任何影响。 - Onkel Toob
我能够在EF Core 2.2和SQL Server 2016中使用PERSISTED(尽管我只尝试过使用DATEADD计算,而没有尝试使用CHECKSUM)。 - Jeremy Cook
显示剩余2条评论
3个回答

10

EF Core 5 开始,HasComputedColumnSql 方法新增了一个可选参数 bool? stored,用于指定该列是否应该被持久化:

modelBuilder.Entity<SomeClass>()
  .Property(p => p.Checksum)
  .HasComputedColumnSql("checksum([FirstColumnName], [SecondColumnName])", stored: true);

5

经过一些阅读和测试,我最终尝试在SQL查询中使用PERSISTED,结果成功了。

entity.Property(e => e.Duration_ms)
      .HasComputedColumnSql("DATEDIFF(MILLISECOND, 0, duration) PERSISTED");

生成的迁移如下:
migrationBuilder.AddColumn<long>(
            name: "duration_ms",
            table: "MyTable",
            nullable: true,
            computedColumnSql: "DATEDIFF(MILLISECOND, 0, duration) PERSISTED");

为了检查数据库是否已经持久化,我运行了以下命令:

SELECT * FROM database_table;

select is_persisted, name from sys.computed_columns where is_persisted = 1

我创建的列已经存在。


谢谢。您正在使用哪个版本的EF Core? - Onkel Toob
@OnkelToob 2.0.1 - Daniel S.
1
那么它应该在我的代码中也能工作(请参见我的帖子的更新部分)。我唯一能发现的区别是:你没有使用任何括号。不幸的是,与此同时我们已经继续前进,所以我不能再回到这个话题了。 - Onkel Toob

3

您还可以指定计算列的存储方式(有时称为持久化),这意味着它在每次行更新时进行计算,并与常规列一起存储在磁盘上:

modelBuilder.Entity<SomeClass>(entity =>
{
     entity.Property(p => p.Checksum)
    .HasComputedColumnSql("(checksum([FirstColumnName], [SecondColumnName]), stored: true);
});

这是从Microsoft Docs中获取的(并稍作修改):https://learn.microsoft.com/en-us/ef/core/modeling/generated-properties?tabs=data-annotations#computed-columns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接