可以通过实体框架迁移将 DateTime 字段默认为 GETDATE() 吗?

问题描述 投票:0回答:9

我将 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
命令的时间。

entity-framework-4 ef-code-first entity-framework-migrations
9个回答
99
投票

你可以使用

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 中的模型。答案建议您修改生成的脚本,以便在创建数据库时添加默认值。

您可以在此处阅读有关实体框架代码优先迁移的更多信息。


25
投票

我最近在 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();
    }
}

我希望这有帮助


15
投票

您必须在

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 中添加,其中已包含迁移)。


7
投票

创建迁移:

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
命令时的日期时间,对于新记录,它将设置创建的日期时间


1
投票

或者,如果您的实体继承自公共接口,您可以重写 DbContext 上的 SaveChanges 方法,并在此时设置或更新属性(非常适合创建日期和上次更改日期)


1
投票

这是最简单的方法。

首先添加

DatabaseGeneratedOption.Computed
DataAnnotion
到您的财产

现在您可以修改 de

SqlServerMigrationSqlGenarator
,覆盖 Genarate 方法并设置
DefaultValueSql = "GETDATE()" or "GETUTCDATE()";


1
投票

在 .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");
    }
}

0
投票

改进:检查约束是否存在:

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");

0
投票

很遗憾,无法通过数据模型对象中的标签来完成。

© www.soinside.com 2019 - 2024. All rights reserved.