Возможно ли по умолчанию использовать поле DateTime для GETDATE() с миграциями Entity Framework?

Я добавил EntityFramework.Migrations(Beta 1) в существующее приложение Code-First, которое проходит через некоторые изменения (как для возможностей миграции, так и для более тонкой настройки таблиц, которые я генерирую из моего первого кода кода) и запускал в сценарий GETDATE().

Я уже использовал собственный класс инициализатора в своем DbContext для запуска SQL-скриптов для установки некоторых полей и создания индексов в моей базе данных. Несколько моих скриптов AlterTable являются первичными для установки полей со значениями по умолчанию (например, для определенных полей DateTime устанавливается значение GETDATE()). Я действительно надеялся, что EntityFramework.Migrations будет отвечать за это, так как вы можете легко указать defaultValue, но пока я не вижу его.

Любые идеи? Я действительно надеялся, что выполнение следующего будет волшебным образом работать. (В конце концов, это "волшебный единорог" )

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

К сожалению, и логически, оно установило мое значение по умолчанию на время, когда была выполнена команда Update-Database.

Ответ 1

Для установки значения по умолчанию вы должны использовать собственный метод SQL script в Up:

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

Установка значения по умолчанию в коде допускает только статические значения - нет функций уровня базы данных.

В любом случае настройка его в конструкторе POCO является правильным способом, если вы сначала будете использовать код. Также, если вы хотите установить значение в приложении для некоторых особых случаев, вы не можете использовать значение по умолчанию в базе данных, потому что значение по умолчанию в базе данных требует либо DatabaseGeneratedOption.Identity, либо DatabaseGeneratedOption.Computed. Обе эти опции позволяют устанавливать свойство только в базе данных.

Edit:

Поскольку продукт все еще находится в разработке, мой ответ больше не действителен. Проверьте @gius ответ на фактический способ достижения этого требования, используя defaultValueSql (он не был доступен в EF Migrations Beta 1, но был добавлен в EF 4.3 Beta 1, который уже включает в себя миграции).

Ответ 2

Вы можете использовать

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, связанных с миграциями. Ответ подсказывает, что вы несколько изменили сгенерированный script, так что добавляется значение по умолчанию при создании базы данных.

Подробнее о Entity Framework Code First Migrations здесь.

Ответ 3

Недавно я столкнулся с этой проблемой в EF6 (так как они еще не исправили ее). Самый простой способ, которым я нашел это, без необходимости вручную модифицировать класс миграции, - переопределить CodeGenerator в вашем классе конфигурации.

Создав класс, который реализует 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();
    }
}

Я надеюсь, что это поможет

Ответ 4

Создать перенос:

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, для новых записей будет установлено datetime создания

Ответ 5

В качестве альтернативы, если ваши объекты наследуются от общего интерфейса, вы можете переопределить метод SaveChanges в DbContext и установить или обновить свойства в этот момент (отлично подходит для "Дата создания и дата последнего изменения" )

Ответ 6

Это самый простой способ.

Сначала добавьте DatabaseGeneratedOption.Computed DataAnnotion в ваше свойство

и теперь вы можете изменить de SqlServerMigrationSqlGenarator, переопределить метод Genarate и установить DefaultValueSql = "GETDATE()" or "GETUTCDATE()";

Ответ 7

Улучшение: проверьте, существует ли ограничение:

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