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

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

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

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

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

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


person adammokan    schedule 21.12.2011    source источник
comment
Я не собираюсь вдаваться в теории, вращающиеся вокруг «...используя ORM, вы не должны помещать логику в базу данных...». Я это понимаю. Просто хочу убедиться, что никто не возвращает пример POCO с DateTime.Now в конструкторе :)   -  person adammokan    schedule 21.12.2011
comment
просто было похожее, наверное, и нашел хорошее решение. Надеюсь, это поможет: stackoverflow.com/questions/9830216/   -  person Thiago Silva    schedule 26.03.2012


Ответы (7)


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

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

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

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

Изменить:

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

person Ladislav Mrnka    schedule 21.12.2011
comment
Спасибо. У меня уже была функциональность SQL Script до перехода на EF.Migrations, так что это мне особо не помогает. - person adammokan; 23.12.2011
comment
Я отметил это как ответ. Не тот ответ, на который я надеялся, но я полагал, что это так. - person adammokan; 23.12.2011

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

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

Подробнее о первой миграции кода Entity Framework можно прочитать здесь.

person gius    schedule 30.01.2012
comment
куда девается этот код?? что такое переменная с?? Можете ли вы предоставить немного больше контекста для этой строки кода? - person Thiago Silva; 22.03.2012
comment
@ThiagoSilva Добавил к ответу немного дополнительного контекста, как вы и предложили. - person Christofer Eliasson; 10.10.2012
comment
самый простой ответ. - person Tracy Zhou; 14.08.2018

Недавно я столкнулся с этой проблемой в EF6 (поскольку они до сих пор ее не исправили). Я нашел самый простой способ сделать это без необходимости вручную изменять класс Migration — переопределить CodeGenerator в вашем классе Configuration.

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

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

person JonnySchnittger    schedule 09.01.2014
comment
Хотел бы я присудить вам специальную награду за это. Я беру существующий проект, который начал свою жизнь как проект nHibernate, который затем был изменен на EF6. Они не приложили дополнительных усилий, чтобы получить все о миграции из-за нехватки времени, что я собираюсь сделать сейчас. Это позволило удалить несколько одноразовых скриптов, которые они запускали в своем самодельном генераторе БД. Спасибо! - person Andy_Vulhop; 04.05.2015
comment
@JonnySchnittger Переопределение MigrationCodeGenerator — отличная идея. Но почему бы не получить атрибут/аннотации столбцов (полей класса) со значением по умолчанию ([DefVal(getutcdate())]) вместо того, чтобы делать это для всех столбцов. Является ли это возможным? Потому что я пытаюсь, но не могу... - person Alex; 24.03.2018
comment
В итоге мне пришлось использовать SqlServerMigrationSqlGenerator, так как я использую автоматические миграции, но мне не нужно было много менять - person pwhe23; 15.07.2020

Создайте миграцию:

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

person Nina    schedule 22.07.2016

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

person Betty    schedule 25.12.2011

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

Сначала добавьте DatabaseGeneratedOption.Computed DataAnnotion к своему ресурсу

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

person Fesaro    schedule 17.04.2015
comment
Этот ответ, вероятно, должен включать конкретный пользовательский SqlServerMigrationSqlGenerator. Ответ JonnySchnittger представляет собой широкую кисть и не включает поддержку аннотаций атрибутов для каждого свойства или плавных определений конфигурации. (Если я в конечном итоге реализую это вместо изменения миграции, я отредактирую ее) - person JoeBrockhaus; 31.08.2015

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

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");
person Martin Staufcik    schedule 17.02.2016