Возможно ли по умолчанию использовать поле 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");