使用Entity Framework迁移,是否可以将DateTime字段默认设置为GETDATE()?

39

我向现有的Code-First应用程序添加了EntityFramework.Migrations(Beta 1),该应用程序正在进行一些更改(包括迁移能力和对从我的代码优先API生成的表进行更加微调)并遇到GETDATE()的情况。

我已经在我的DbContext中使用自定义初始化程序类来运行SQL脚本,在数据库中设置一些字段并创建索引。我的AlterTable脚本中有几个主要是为了设置具有默认值的字段(例如将某些DateTime字段设置为GETDATE())。我真的希望EntityFramework.Migrations会有一个解决方案,因为您可以轻松地指定defaultValue,但到目前为止我还没有看到一个。

有什么想法吗?我真的很希望做以下操作会自动工作。(毕竟这是“魔法独角兽”)

DateCreated = c.DateTime(nullable: false, defaultValue: DateTime.Now)

不幸的是,从逻辑上讲,它将我的默认值设置为执行 Update-Database 命令时的时间。


我不想深入探讨“……通过使用ORM,您不应该将逻辑放入数据库中……”这些理论。我明白了。只是想确保没有人会在构造函数中抛出一个DateTime.Now的POCO示例 :) - adammokan
刚遇到了类似的问题,找到了一个不错的解决方案。希望能帮到你: https://dev59.com/vGHVa4cB1Zd3GeqPq9tD - Thiago Silva
8个回答

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日:

根据Thiago在评论中的要求,我添加了一些额外的上下文。

上面的代码是通过在包管理器控制台中运行Add-Migration MyMigration命令生成的EF Migrations迁移文件。生成的代码基于与迁移相关联的DbContext中的模型。答案建议您修改生成的脚本,以便在创建数据库时添加默认值。

您可以在此处阅读有关Entity Framework Code First Migrations的更多信息


1
这段代码应该放在哪里?变量“c”是什么?你能提供一下这行代码的更多上下文吗? - Thiago Silva
1
@ThiagoSilva 根据您的建议,我在答案中添加了一些额外的上下文。 - Christofer Eliasson
最简单的答案。 - Tracy Zhou

25

我最近在EF6中遇到了这个问题(因为他们仍然没有解决它)。我发现最简单的方法是在您的配置类中覆盖CodeGenerator,而无需手动修改Migration类。

通过创建一个实现MigrationCodeGenerator的类,然后重写Generate方法,您可以遍历所有操作并应用所需的任何修改。

一旦完成修改,您可以初始化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();
    }
}

我希望这可以帮到你。


1
我希望我能为你颁发一个临时的声望奖励,因为你做得非常好。我正在接手一个已经开始作为nHibernate项目的现有项目,然后有人将其更改为EF6。由于时间限制,他们没有走额外的路线来完成所有迁移工作,而我现在打算这样做。这使得我可以删除他们在手动生成的数据库中运行的几个一次性脚本。谢谢! - Andy_Vulhop
@JonnySchnittger 覆盖MigrationCodeGenerator是个好主意。但为什么不尝试获取默认值([DefVal("getutcdate()")])的列(类字段)的属性/注释,而非所有列。这种做法可行吗?我尝试了但无法实现... - Alex
我最终需要使用SqlServerMigrationSqlGenerator,因为我使用了自动迁移,但我不需要改变太多。 - pwhe23

15

你必须在Up方法中使用自定义SQL脚本来设置默认值:

Sql("ALTER TABLE TableName ADD CONSTRAINT ConstraintName DEFAULT GETDATE() FOR ColumnName");

在代码中设置默认值只允许静态值 - 没有数据库级别的函数。

无论如何,在POCO构造函数中设置它是正确的方式,如果您要使用Code First。此外,如果您希望在应用程序中为某些特殊情况设置值,则不能在数据库中使用默认值,因为数据库中的默认值需要DatabaseGeneratedOption.IdentityDatabaseGeneratedOption.Computed。这两个选项仅允许在数据库中设置属性。

编辑:

由于产品仍在开发中,我的答案已经不再有效。请参阅@gius的答案,以了解通过使用defaultValueSql实现此要求的实际方法(它在EF Migrations Beta 1中不可用,但已添加到EF 4.3 Beta 1中,该版本已包括迁移)。


谢谢。在转移到EF.Migrations之前,我已经实现了SQL脚本功能,所以这并没有对我有什么帮助。 - adammokan
我将其标记为答案。虽然不是我所希望的答案,但我认为情况就是如此。 - adammokan

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

使用Entity Framework和.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");
    }
}

这是最省力的答案,谢谢。顺便说一下,它也适用于 .net 5。 - kirbby

1
这是最简单的方法。
首先,在您的属性中添加DatabaseGeneratedOption.Computed DataAnnotion 现在,您可以修改SqlServerMigrationSqlGenarator,重写Generate方法并设置DefaultValueSql = "GETDATE()"或"GETUTCDATE()";

1
这个答案可能需要包括特定的自定义SqlServerMigrationSqlGenerator。JonnySchnittger的答案是笼统的,不包括每个属性的属性注释或流畅的配置定义支持。(如果我最终实现这个而不是修改迁移,我会编辑它) - JoeBrockhaus

1

或者,如果您的实体继承自一个公共接口,您可以在DbContext上覆盖SaveChanges方法,并在那个时候设置或更新属性(非常适合创建日期和最后更改日期)。


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接