代码优先迁移:如何为新属性设置默认值?

76

我正在使用EF6将report类的实例存储在我的数据库中。数据库已经包含数据。假设我想要向report添加属性,

public class report {
    // ... some previous properties

    // ... new property:
    public string newProperty{ get; set; }
}

现在,如果我转到包管理器控制台并执行以下操作
add-migration Report-added-newProperty
update-database

我会在'/Migrations'文件夹中得到一个文件,该文件将向表中添加一个newProperty列。这部分工作正常。然而,在数据库中的旧条目中,newProperty的值现在为空字符串。但我希望它是"old"之类的值。
所以我的问题是:如何在迁移脚本(或其他地方)中为新属性(任何类型)设置默认值?

1
这里的行为不是由实体框架引起的,而是由TSQL引起的。当您在TSQL中添加一个新的可空列时,必须使用WITH VALUES来指定现有记录的值(请参见此问题)。这里答案中给出的解决方法是将该列设置为非空。另一种选择是定义一个自定义的DefaultValue属性,例如这个 - Hans Vonn
5个回答

138
如果您查看生成的迁移代码,您将看到AddColumn
AddColumn("dbo.report", "newProperty", c => c.String(nullable: false));

你可以添加defaultValue

AddColumn("dbo.report", "newProperty", 
           c => c.String(nullable: false, defaultValue: "old"));

或者添加 defaultValueSql

AddColumn("dbo.report", "newProperty",
           c => c.String(nullable: false, defaultValueSql: "GETDATE()"));

26
如果它能正确地拉取代码优先模型中定义的[DefaultValue(xyz)]属性,那就太好了。而不是每次添加迁移时都要记住哪些列需要什么,然后必须对生成的文件进行后期编辑,再对更改后的文件运行Update-Database命令。我想知道是否可能覆盖这种不智能的行为…… 我无法相信没有人改进过这个问题。毕竟默认的AspNet Identity模型中,LockoutEnabled、EmailConfirmed和AccessFailedCount都是硬编码的,并且都是问题。 - Barry
5
这似乎不适用于MySQL-EF。 'defaultValue' 不设置值,而 'defaultValueSql' 会创建异常(“blob text geometry or json column can't have a default value”)... 唯一剩下的解决方法是使用原始SQL。 :( - Efrain
对于像我一样需要枚举默认值的人: AddColumn("dbo.report", "newEnumProperty", c => c.Int(nullable: false, defaultValue: 0)); //使用整数来设置默认枚举值 - EeKay
1
引用@Efrain的话,使用EF Core 5和MySQL时,我得到了空字段。默认值没有应用。 - StefanoV
2
如何添加其他列的默认值,例如:我希望新属性的默认值为report.OldProperty? - user1735921
同样,这仅适用于迁移。我担心它会为未来的插入操作设置默认值,但经过测试,情况并非如此。如果您确实希望为新插入的字段设置默认值,请使用模型进行设置。 - Luke Vo

22
希望能对某人有所帮助。从之前的回答中整合所有内容(使用布尔属性的示例):
1)向实体添加一个新属性。
/// <summary>
/// Determines if user is enabled or not. Default value is true
/// </summary>
public bool IsEnabled { get; set; }

2) 运行以下命令将新更改添加到迁移中。
add-migration addIsEnabledColumn

3) 从上述命令创建一个迁移文件,打开该文件。

enter image description here

4) 设置默认值。
public override void Up()
{
    AddColumn(
        "dbo.AspNetUsers", 
        "IsEnabled", 
        c => c.Boolean(
            nullable: false, 
            defaultValue: true
        )
    );
}

4
在第四步之后运行Update-Database命令,以将更改实际更新到数据库中。为了方便新手,添加此点提示。 - vinmm
默认值是否可以是其他字段的函数? - RJFalconer

7
您需要修改迁移脚本中添加属性/列的行,如下所示:
AddColumn("dbo.reports", "newProperty", c => c.String(nullable: false, defaultValue: "test"));

-2

我通过重写SaveChanges方法解决了这个问题。请参见下面的解决方案。

  1. 解决方案说明

    i) 在DbContext类中重写SaveChanges方法。

    public override int SaveChanges()
    {
        return base.SaveChanges();
    }
    

    ii) 编写逻辑以设置默认值

    public override int SaveChanges()
    {
        //为您的属性设置默认值
        foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("YOUR_PROPERTY") != null))
        {
            if (entry.State == EntityState.Added)
            {
                if (entry.Property("YOUR_PROPERTY").CurrentValue == null)
                    entry.Property("YOUR_PROPERTY").CurrentValue = YOUR_DEFAULT_VALUE;
            }
        }
    
        return base.SaveChanges();
    }
    
  2. 示例

    public override int SaveChanges()
    {
        //为RegistedDate属性设置默认值
        foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("RegistedDate") != null))
        {
            if (entry.State == EntityState.Added)
            {
                if ((DateTime)entry.Property("RegistedDate").CurrentValue == DateTime.MinValue)
                    entry.Property("RegistedDate").CurrentValue = DateTime.Now;
            }
        }
    
        //为IsActive属性设置默认值
        foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("IsActive") != null))
        {
            if (entry.State == EntityState.Added)
            {
                if(entry.Property("IsActive").CurrentValue == null)
                    entry.Property("IsActive").CurrentValue = false;
            }
        }
    
        return base.SaveChanges();
    }
    

-7

我发现在实体属性上只使用自动属性初始化器就足以完成工作。

例如:

public class Thing {
    public bool IsBigThing { get; set; } = false;
}

3
问题在于它实际上没有在数据库中设置默认值。 - chipples

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