代码优先迁移:如何为新属性设置默认值?
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”值)?