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

Posted

技术标签:

【中文标题】代码优先迁移:如何为新属性设置默认值?【英文标题】:Code-first migration: How to set default value for new property? 【发布时间】:2015-09-10 14:54:00 【问题描述】:

我正在使用 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 的值现在是一个空字符串。但我希望它是,例如,“旧的”。

所以我的问题是:如何在迁移脚本(或其他地方)中为新属性(任何类型)设置默认值?

【问题讨论】:

这里的行为不是由于实体框架而是TSQL。当您在 TSQL 中添加一个新的可空列时,必须使用 WITH VALUES 来指定现有记录的值(请参阅此 question)。此处答案中给出的解决方法是使该列不可为空。另一种选择是定义自定义 DefaultValue 属性,例如 this。 【参考方案1】:

如果您看到生成的迁移代码,您将看到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()"));

【讨论】:

如果它正确地拉入代码优先模型中定义的 [DefaultValue(xyz)] 属性会很好。每次添加迁移时不必记住哪些列需要什么;必须对生成的文件进行后期编辑,然后针对该更改的文件正常运行更新数据库。我想知道是否有可能覆盖这种不明智的行为……我不敢相信没有人对此有所改进。当然,我不是唯一一个对默认 AspNet 身份模型感到沮丧的人... LockoutEnabled、EmailConfirmed、AccessFailedCount。所有硬编码,所有问题 这似乎不适用于 mysql-EF。 'defaultValue' 没有设置值,并且 'defaultValueSql' 会创建一个异常(“blob 文本几何或 json 列不能有默认值”)......剩下的唯一解决方法是使用原始 SQL。 :( 对于那些像我一样需要枚举默认值的人: AddColumn("dbo.report", "newEnumProperty", c => c.Int(nullable: false, defaultValue: 0)); //使用整数设置默认枚举值 引用@Efrain,使用 EF Core 5 和 MySQL 我得到了空字段。不应用默认值。 如何添加其他列的默认值,例如:我希望新的属性默认值为report.OldProperty?【参考方案2】:

希望它可以帮助某人。将之前答案中的所有内容放在一起(使用布尔属性的示例)

1) 向实体添加新属性。

/// <summary>
/// Determines if user is enabled or not. Default value is true
/// </summary>
public bool IsEnabled  get; set; 

2) 运行以下命令以在迁移中添加新更改。

add-migration addIsEnabledColumn

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

4) 设置默认值。

public override void Up()

        AddColumn(
            "dbo.AspNetUsers", 
            "IsEnabled", 
            c => c.Boolean(
                nullable: false, 
                defaultValue: true
            )
        );

【讨论】:

5) 在第 4 步之后运行 Update-Database 以使用更改更新实际数据库。添加这一点只是为了新手。 是否可以让默认值成为其他字段的函数?【参考方案3】:

您必须更改迁移脚本中添加属性/列的行,如下所示:

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

【讨论】:

【参考方案4】:

我通过覆盖 SaveChanges 方法解决了这个问题。请参阅下面的解决方案。

    解决方案说明

    i) 覆盖 DbContext 类中的 SaveChanges 方法。

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

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

    public override int SaveChanges()
    
        //set default value for your property
        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();
    
    

    示例

    public override int SaveChanges()
    
        //set default value for RegistedDate property
        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;
            
        
    
        //set default value for IsActive property
        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();
    
    

【讨论】:

【参考方案5】:

我发现仅对实体属性使用 Auto-Property Initializer 就足以完成工作。

例如:

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

【讨论】:

问题是它实际上并没有在数据库中设置默认值。

以上是关于代码优先迁移:如何为新属性设置默认值?的主要内容,如果未能解决你的问题,请参考以下文章

如何为所有远程登录的用户设置默认进程调度优先级(“nice”值)?

Angular - 如何为可选输入属性设置默认值

如何为 Laravel / Eloquent 模型设置默认属性值?

C#:如何为部分类中的属性设置默认值?

将默认值设置为列代码优先方法[重复]

代码优先迁移中列的 EF6 Oracle 默认值