我将 EntityFramework.Migrations(Beta 1)添加到现有的 Code-First 应用程序中,该应用程序正在经历一些更改(对于迁移功能以及对我从代码优先 API 生成的表进行更多微调),并遇到了GETDATE() 场景。
我已经在 DbContext 中使用自定义初始值设定项类来运行 SQL 脚本来设置一些字段并在数据库中创建索引。我的一些 AlterTable 脚本主要用于设置具有默认值的字段(例如将某些日期时间字段设置为 GETDATE())。我真的希望 EntityFramework.Migrations 能够对此有一个答案,因为您可以轻松指定 defaultValue,但到目前为止我还没有看到一个答案。
有什么想法吗?我真的希望执行以下操作会神奇地起作用。 (毕竟它是“神奇的独角兽”)
DateCreated = c.DateTime(nullable: false, defaultValue: DateTime.Now)
不幸的是,从逻辑上讲,它将我的默认值设置为执行
Update-Database
命令的时间。
你可以使用
DateCreated = c.DateTime(nullable: false, defaultValueSql: "GETDATE()")
用途:
public partial class MyMigration : DbMigration
{
public override void Up()
{
CreateTable("dbo.Users",
c => new
{
Created = c.DateTime(nullable: false, defaultValueSql: "GETDATE()"),
})
.PrimaryKey(t => t.ID);
...
2012-10-10更新:
按照蒂亚戈在评论中的要求,我添加了一些额外的上下文。
上面的代码是 EF Migrations 通过在包管理器控制台中运行
Add-Migration MyMigration
作为命令生成的迁移文件。生成的代码基于与迁移关联的 DbContext 中的模型。答案建议您修改生成的脚本,以便在创建数据库时添加默认值。
您可以在此处阅读有关实体框架代码优先迁移的更多信息。
我最近在 EF6 中遇到了这个问题(因为他们还没有修复它)。我发现无需手动修改 Migration 类的最简单方法是重写 Configuration 类中的 CodeGenerator。
通过创建一个实现 MigrationCodeGenerator 的类,然后重写生成方法,您可以迭代所有操作并应用您想要的任何修改。
完成修改后,您可以初始化 CSharpMigrationCodeGenerator 并返回其默认值。
public class ExtendedMigrationCodeGenerator : MigrationCodeGenerator
{
public override ScaffoldedMigration Generate(string migrationId, IEnumerable<MigrationOperation> operations, string sourceModel, string targetModel, string @namespace, string className)
{
foreach (MigrationOperation operation in operations)
{
if (operation is CreateTableOperation)
{
foreach (var column in ((CreateTableOperation)operation).Columns)
if (column.ClrType == typeof(DateTime) && column.IsNullable.HasValue && !column.IsNullable.Value && string.IsNullOrEmpty(column.DefaultValueSql))
column.DefaultValueSql = "GETDATE()";
}
else if (operation is AddColumnOperation)
{
ColumnModel column = ((AddColumnOperation)operation).Column;
if (column.ClrType == typeof(DateTime) && column.IsNullable.HasValue && !column.IsNullable.Value && string.IsNullOrEmpty(column.DefaultValueSql))
column.DefaultValueSql = "GETDATE()";
}
}
CSharpMigrationCodeGenerator generator = new CSharpMigrationCodeGenerator();
return generator.Generate(migrationId, operations, sourceModel, targetModel, @namespace, className);
}
}
internal sealed class Configuration : DbMigrationsConfiguration<Project.Models.Context.DatabaseContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
MigrationsDirectory = @"Migrations";
this.CodeGenerator = new ExtendedMigrationCodeGenerator();
}
}
我希望这有帮助
您必须在
Up
方法中使用自定义SQL脚本来设置默认值:
Sql("ALTER TABLE TableName ADD CONSTRAINT ConstraintName DEFAULT GETDATE() FOR ColumnName");
在代码中设置默认值仅允许静态值 - 没有数据库级函数。
无论如何,如果您要先使用代码,在 POCO 构造函数中设置它是正确的方法。此外,如果您想在某些特殊情况下在应用程序中设置值,则不能使用数据库中的默认值,因为数据库中的默认值需要
DatabaseGeneratedOption.Identity
或 DatabaseGeneratedOption.Computed
。这两个选项都允许仅在数据库中设置属性。
编辑:
由于产品仍在开发中,我的答案不再有效。使用
defaultValueSql
检查 @gius 答案以了解实现此要求的实际方法(它在 EF Migrations Beta 1 中不可用,但已在 EF 4.3 Beta 1 中添加,其中已包含迁移)。
创建迁移:
public partial class Table_Alter : DbMigration
{
public override void Up()
{
AddColumn("dbo.tableName", "columnName",
c => c.DateTime(nullable: false, defaultValueSql: "GETDATE()"));
}
public override void Down()
{
DropColumn("dbo.tableName", "columnName");
}
}
对于现有记录,它将设置您运行
Update-Database
命令时的日期时间,对于新记录,它将设置创建的日期时间
或者,如果您的实体继承自公共接口,您可以重写 DbContext 上的 SaveChanges 方法,并在此时设置或更新属性(非常适合创建日期和上次更改日期)
这是最简单的方法。
首先添加
DatabaseGeneratedOption.Computed
DataAnnotion
到您的财产
现在您可以修改 de
SqlServerMigrationSqlGenarator
,覆盖 Genarate 方法并设置 DefaultValueSql = "GETDATE()" or "GETUTCDATE()";
在 .net 6 中使用实体框架,我可以通过将
defaultValue
更改为 defaultValueSql
来使迁移添加默认时间:
public partial class ReportQueueAddCreatedAt : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "CreatedAt",
table: "ReportQueue",
type: "datetime2",
nullable: false,
defaultValueSql: "GETDATE()");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CreatedAt",
table: "ReportQueue");
}
}
改进:检查约束是否存在:
Sql(@"
if not exists (
select *
from sys.all_columns c
join sys.tables t on t.object_id = c.object_id
join sys.schemas s on s.schema_id = t.schema_id
join sys.default_constraints d on c.default_object_id = d.object_id
where
d.name = 'DF_ThubOutputEmail_Created'
)
begin
ALTER TABLE dbo.ThubOutputEmails ADD CONSTRAINT DF_ThubOutputEmail_Created default getdate() for Created;
end");
很遗憾,无法通过数据模型对象中的标签来完成。