如何在EF Code First中创建持久化计算列?

9

如何将此列设置为数据库中的PERSISTED COMPUTED列?

我的当前尝试(它加载所有带有seed中null的CompCol行):

    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
@InteXX - 我记得在(非常)旧版本的SQL中,一些“持久化”或“计算”的方面需要企业版。但是,我想我记错了。我已经删除了我的评论! - Glen Little
3个回答

9
我找到的解决方案是:

  1. Make sure auto migrations are turned off. This is so that VS will generate a script (fluent api code) for us to further customise instead of just running it. So in the configuration class :

    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }
    
  2. Add the field to the class and set it as computed like so, the setter is private because we obviously cannot write to a computed field :

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public string BreakdownNo { get; private set; }
    
  3. Then do an add-migration [xyz-name] in the Package Manager Console to generate the migration code, which will appear under the migrations folder with the given name.

  4. Inside the migration comment out the code in Up() and add custom SQL like so :

    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. Do an update-database in the PM and it should add the computed column properly.

进一步说明:如果你的公式有误,那么你就需要通过执行update-database -targetMigration: [回到的迁移名称]命令来回滚迁移,然后再执行add-migration name命令来修改你的公式,最后用update-database命令结束。可能还有更好的方法,但这是我找到并使用的方法。

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


5
这是一个很好的例子,说明自动迁移操作的难度有多大。 - Glen Little
你不能只需在ALTER TABLE语句中包含关键字PERSISTED,就将其设置为持久化吗?文档 - InteXX
更新:是的,这个可以运行。我们有一个持久化计算列。例如:ALTER TABLE [LogEntries] ADD [ReportTime] AS (DATEADD(HOUR, -7, [LogTime])) PERSISTED - InteXX

1
为什么不这样调用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)");
    }
}

0

我在使用被接受答案中提出的方法时遇到了一些问题。我提供了一个替代方案,这个方案对我很有效。

当运行这个查询时,我遇到了一个失败:

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

错误信息:

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

我们可以看到,EF 6.2正在尝试向属性写入一个值。这是否是因为EF内部尝试写入“私有集合”导致的,我不知道。但最终结果是失败了。

与其将列设置为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关键字在此处有效。这将变成一个持久化的计算列。

你的经验可能会有所不同。

--编辑--

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

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的帮助。


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