我已将 [Required]
数据注释添加到 ASP.NET MVC application 中的一个模型中。创建迁移后,运行 Update-Database
命令会导致以下错误:
无法将值 NULL 插入列“导演”,表“MOVIES_cf7bad808fa94f89afa2e5dae1161e78.dbo.Movies”;列不允许空值。更新失败。该语句已终止。
这是由于某些记录在其 Director
列中具有 NULL。如何自动将这些值更改为某个默认值(比如“John Doe”)导演?
这是我的模型:
public class Movie
{
public int ID { get; set; }
[Required]
public string Title { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Required]
public string Genre { get; set; }
[Range(1,100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[StringLength(5)]
public string Rating { get; set; }
[Required] /// <--- NEW
public string Director { get; set; }
}
这是我最新的迁移:
public partial class AddDataAnnotationsMig : DbMigration
{
public override void Up()
{
AlterColumn("dbo.Movies", "Title", c => c.String(nullable: false));
AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false));
AlterColumn("dbo.Movies", "Rating", c => c.String(maxLength: 5));
AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false));
}
public override void Down()
{
AlterColumn("dbo.Movies", "Director", c => c.String());
AlterColumn("dbo.Movies", "Rating", c => c.String());
AlterColumn("dbo.Movies", "Genre", c => c.String());
AlterColumn("dbo.Movies", "Title", c => c.String());
}
}
除了来自@webdeveloper 和@Pushpendra 的回答之外,您还需要手动向迁移添加更新以更新现有行。例如:
public override void Up()
{
Sql("UPDATE [dbo].[Movies] SET Title = 'No Title' WHERE Title IS NULL");
AlterColumn("dbo.Movies", "Title", c => c.String(nullable: false,defaultValue:"MyTitle"));
}
这是因为 AlterColumn
生成 DDL 以将列的默认值设置为表规范中的某个特定值。 DDL 不会影响数据库中的现有行。
您实际上同时进行了两项更改(设置默认值并使列 NOT NULL)并且它们中的每一个都单独有效,但是由于您同时进行了两项更改,因此您可以期望系统'智能地'实现您的意图并将所有 NULL
值设置为默认值,但这并不是一直期望的。
假设您只是设置列的默认值,而不是使其变为 NOT NULL。您显然不希望使用您提供的默认值更新所有 NULL 记录。
所以,在我看来,这不是一个错误,我不希望 EF 以我没有明确告诉它这样做的方式更新我的数据。开发人员负责指示系统如何处理数据。
如果我没记错的话,这样的事情应该可以工作:
AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false, defaultValueSql: "'John Doe'"));
注意:defaultValueSql 参数值被视为逐字 SQL 语句,因此如果所需的值实际上是一个字符串,如 John Doe 示例,则需要在值周围加上单引号。
IS NULL
更新行。
"'John Doe'"
- 您需要使用 SQL 引号。
AlterColumn
会更新当前值?它是一个 DDL(不是 DML)命令。
public partial class AddDataAnnotationsMig : DbMigration
{
public override void Up()
{
AlterColumn("dbo.Movies", "Title", c => c.String(nullable: false,defaultValue:"MyTitle"));
AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false,defaultValue:"Genre"));
AlterColumn("dbo.Movies", "Rating", c => c.String(maxLength: 5));
AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false,defaultValue:"Director"));
}
public override void Down()
{
AlterColumn("dbo.Movies", "Director", c => c.String());
AlterColumn("dbo.Movies", "Rating", c => c.String());
AlterColumn("dbo.Movies", "Genre", c => c.String());
AlterColumn("dbo.Movies", "Title", c => c.String());
}
}
不确定此选项是否始终存在,但遇到了类似的问题,发现我能够设置默认值,而无需使用以下命令运行任何手动更新
defaultValueSql: "'NY'"
当提供的值是 "NY"
时出现错误,然后我意识到他们期待像 "GETDATE()"
这样的 SQL 值,所以我尝试了 "'NY'"
并且成功了
整条线看起来像这样
AddColumn("TABLE_NAME", "State", c => c.String(maxLength: 2, nullable: false, defaultValueSql: "'NY'"));
感谢this answer,让我走上了正轨
从 EF Core 2.1 开始,您可以在更改列之前使用 MigrationBuilder.UpdateData
更改值(比使用原始 SQL 更简洁):
protected override void Up(MigrationBuilder migrationBuilder)
{
// Change existing NULL values to NOT NULL values
migrationBuilder.UpdateData(
table: tableName,
column: columnName,
value: valueInsteadOfNull,
keyColumn: columnName,
keyValue: null);
// Change column type to NOT NULL
migrationBuilder.AlterColumn<ColumnType>(
table: tableName,
name: columnName,
nullable: false,
oldClrType: typeof(ColumnType),
oldNullable: true);
}
许多其他响应集中在如何在这些问题发生时进行手动干预。
生成迁移后,对迁移执行以下任一更改: 修改列定义以包含 defaultValue 或 defaultSql 语句:AlterColumn("dbo.Movies", "Director", c => c.String(nullable: false, default :“”));在 AlterColumn 之前注入一条 SQL 语句以预填充现有列: Sql("UPDATE dbo.Movies SET Director = '' WHERE Director IS NULL");
请记住,如果您重新构建迁移,将覆盖应用于迁移脚本的手动更改。对于第一个解决方案,扩展 EF 以在迁移生成过程中自动定义字段的默认值非常容易。
注意:EF 不会自动为您执行此操作,因为每个 RDBMS 提供程序的默认值实现会有所不同,而且还因为默认值在纯 EF 运行时中的意义较小,因为每个行插入都会为每个属性提供当前值,即使它是空的,所以默认值约束永远不会被评估。这个 AlterColumn 语句是唯一一次默认约束发挥作用,我想这对于设计 SQL Server 迁移实现的团队来说是一个较低的优先级。
以下解决方案结合了属性表示法、模型配置约定和列注释,以将元数据传递到自定义迁移代码生成器。如果您不使用属性表示法,则可以将步骤 1 和 2 替换为每个受影响字段的流畅表示法。这里有很多技巧在玩,随意使用部分或全部,希望对这里的每个人都有价值
声明默认值 创建或重新定义现有属性以定义要使用的默认值,在本例中,我们将创建一个名为 DefaultValue 的新属性,该属性继承自 ComponentModel.DefaultValueAttribute,因为它的用法很直观,并且有可能存在代码库已经实现了这个属性。使用此实现,您只需要使用此特定属性来访问 DefaultValueSql,这对于日期和其他自定义场景很有用。实现 [DefaultValue("Insert DefaultValue Here")] [Required] /// <--- NEW public string Director { get;放; } // 默认值示例 sql [DefaultValue(DefaultValueSql: "GetDate()")] [Required] public string LastModified { get;放; } 属性定义命名空间 EFExtensions { ///
我发现仅在实体属性上使用 Auto-Property Initializer 就足以完成工作。
例如:
public class Thing {
public bool IsBigThing { get; set; } = false;
}
出于某种原因,我无法解释自己,批准的答案不再适用于我。
它适用于另一个应用程序,而我正在使用的应用程序却没有。
因此,另一种但效率很低的解决方案是覆盖 SaveChanges() 方法,如下所示。这个方法应该在 Context 类上。
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries().Where(entry => entry.Entity.GetType().GetProperty("ColumnName") != null))
{
if (entry.State == EntityState.Added)
{
entry.Property("ColumnName").CurrentValue = "DefaultValue";
}
}