如何首先在 EF 代码中创建持久计算列?

Posted

技术标签:

【中文标题】如何首先在 EF 代码中创建持久计算列?【英文标题】:How to make persisted computed column in EF code first? 【发布时间】:2013-01-11 17:30:34 【问题描述】:

如何使该列与数据库中的 PERSISTED COMPUTED 列相似?

我当前的尝试(它加载所有 CompCol 行,种子中为 null):

    public class Call
    
        public Call()
        
        

        [Key]
        public int Id  get; set; 

        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public string CompCol
        
            get
            
                return "ABC-" + Convert.ToString(Id).PadLeft(5, '0');
            
            protected set 
        

【问题讨论】:

@GlenLittle — 你能详细说明一下吗?您的描述似乎表明它不仅仅是在语句中添加关键字PERSISTED。 (有关更多信息,请参阅下面的答案。) @InteXX - 我记得在(非常)旧版本的 SQL 中,“持久”或“计算”的某些方面需要企业版。但是,我想我记错了。我删除了我的评论! 【参考方案1】:

我找到的解决方案是:

    确保关闭自动迁移。这样 VS 会生成一个脚本(流利的 api 代码)供我们进一步定制,而不仅仅是运行它。所以在配置类中:

    public Configuration()
    
        AutomaticMigrationsEnabled = false;
    
    

    将字段添加到类并将其设置为计算字段,setter 是私有的,因为我们显然无法写入计算字段:

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public string BreakdownNo  get; private set; 
    

    然后在 Package Manager Console 中执行 add-migration [xyz-name] 以生成迁移代码,该代码将出现在具有给定名称的迁移文件夹下。

    在迁移中注释掉 Up() 中的代码并添加自定义 SQL,如下所示:

    public override void Up()
    
        //AddColumn("dbo.Calls", "BreakdownNo", c => c.String());
        Sql("ALTER TABLE dbo.Calls ADD BreakdownNo AS ('BD'+RIGHT('00000'+ CAST(Id AS VARCHAR), 6))");
    
    

    在 PM 中执行update-database,它应该会正确添加计算列。

进一步说明:如果您弄错了公式,那么您将不得不通过执行 update-database -targetMigration: [name of migration to go back to] 来恢复迁移,然后执行另一个 add-migration name 并在那里修改您的公式,以更新结束-数据库。可能有更好的方法,但这是我发现和使用的。

然而,我还没有找到使该字段持续存在的方法。

【讨论】:

这是一个很好的例子,说明要让自动迁移正常工作是多么困难。 您不能通过在您的ALTER TABLE 语句中简单地包含关键字PERSISTED 来将其设置为持久化吗? Documentation. 更新:是的,这行得通。我们有一个持久的计算列。例如:ALTER TABLE [LogEntries] ADD [ReportTime] AS (DATEADD(HOUR, -7, [LogTime])) PERSISTED【参考方案2】:

为什么不这样调用sql:

public class demo

    void demoMethod()
    
        Model1 model = new Model1();//Model1 : DbContext
        model.Database.ExecuteSqlCommand("alter table Results drop column Total; alter table Results add Total AS (Arabic + English + Math + Science)");
    

【讨论】:

【参考方案3】:

我使用接受的答案中提出的方法遇到了一些麻烦。我提供了一个对我有用的替代解决方案。

我在运行这个查询时遇到了错误:

oDb.LogEntries.SingleOrDefault(Function(LogEntry) LogEntry.LogTime = dDate)

错误信息:

“LogEntry”上的“MinutesOffline”属性无法设置为“System.Int32”值。您必须将此属性设置为“System.Single”类型的非空值。

正如我们所见,EF 6.2 正在尝试将值写入属性。这是否是由于 EF 内部试图写入Private Set,我不知道。它几乎看起来像。但最终结果才是最重要的:语句失败。

我没有将列设置为DatabaseGeneratedOption.Computed,而是完全忽略了它:Builder.Entity(Of LogEntry).Ignore(Function(LogEntry) LogEntry.MinutesOffline)

这使我能够创建一个只读属性:

Public ReadOnly Property MinutesOffline As Single
  Get
    Return IIf(Me.Scale < 1, 5, 0)
  End Get
End Property

它还有一个额外的好处,就是我们不必在生成的迁移中注释掉任何行。

我们仍然需要在Up() 中进行自定义Sql() 调用:

ALTER TABLE [LogEntries] ADD [MinutesOffline] AS (IIF([Scale] < 1, 5, 0)) PERSISTED

...PERSISTED 关键字在这里确实有效。这将成为一个持久计算列。

YMMV

--编辑--

我发现了为什么会出现转换错误;它与迁移无关,与我的代码有关。我在创建时没有正确转换计算列:

ALTER TABLE [LogEntries] ADD [MinutesOffline] AS (IIF([Scale] < 1, 5, 0)) PERSISTED

这样做的正确语法是这样的:

ALTER TABLE [LogEntries] ADD [MinutesOffline] AS (CAST((IIF([Scale] < 1, 5, 0)) AS REAL)) PERSISTED

因此,我已恢复 Ignore() 调用并将所有内容切换回接受的答案中建议的方法。

向JotaBe 致敬以寻求帮助。

【讨论】:

以上是关于如何首先在 EF 代码中创建持久计算列?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 EF 核心在 SQLite 中创建自动增量列?

使用 CASE WHEN 和 IN 在 SQL 中创建计算列

首先使用代码在 MySQL 中创建 NVARCHAR 列?

EF 代码首先告诉我对已经在 db 中的 db 对象进行迁移

无法在Ntier结构中创建EF数据库

如何首先将 EF Core 代码与 azure synapse 一起使用