【问题标题】:How to make persisted computed column in EF code first?如何首先在 EF 代码中创建持久计算列?
【发布时间】: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 中,“持久”或“计算”的某些方面需要企业版。但是,我想我记错了。我删除了我的评论!

标签: sql-server entity-framework code-first entity-framework-migrations


【解决方案1】:

我找到的解决方案是:

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

    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }
    
  2. 将字段添加到类并将其设置为计算字段,setter 是私有的,因为我们显然无法写入计算字段:

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public string BreakdownNo { get; private set; }
    
  3. 然后在 Package Manager Console 中执行 add-migration [xyz-name] 以生成迁移代码,该代码将出现在具有给定名称的迁移文件夹下。

  4. 在迁移中注释掉 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))");
    }
    
  5. 在 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 致敬以寻求帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-08
      • 2012-01-04
      • 2018-09-18
      • 2012-01-10
      • 1970-01-01
      • 2013-10-11
      • 2011-10-06
      • 2013-10-07
      相关资源
      最近更新 更多