可以使用实体框架迁移将 DateTime 字段默认为 GETDATE() 吗?
Posted
技术标签:
【中文标题】可以使用实体框架迁移将 DateTime 字段默认为 GETDATE() 吗?【英文标题】:Possible to default DateTime field to GETDATE() with Entity Framework Migrations? 【发布时间】:2012-01-25 12:53:26 【问题描述】:我将 EntityFramework.Migrations (Beta 1) 添加到现有的 Code-First 应用程序中,该应用程序正在进行一些更改(用于迁移功能和对我从我的代码优先 API 生成的表进行更多微调)并运行进入 GETDATE() 场景。
我已经在我的 DbContext 中使用了一个自定义初始化程序类来运行 SQL 脚本来设置一些字段并在我的数据库中创建索引。我的一些 AlterTable 脚本主要用于设置具有默认值的字段(例如某些 DateTime 字段被设置为 GETDATE())。我真的希望 EntityFramework.Migrations 对此有答案,因为您可以轻松指定 defaultValue,但到目前为止我还没有看到。
有什么想法吗?我真的希望执行以下操作会神奇地起作用。 (毕竟是‘魔法独角兽’)
DateCreated = c.DateTime(nullable: false, defaultValue: DateTime.Now)
不幸的是,从逻辑上讲,它将我的默认值设置为执行Update-Database
命令的时间。
【问题讨论】:
我不打算研究围绕“......通过使用 ORM,您不应该将逻辑放入数据库......”的理论。我明白那个。只是想确保没有人在构造函数中使用 DateTime.Now 抛出 POCO 示例:) 刚刚有类似的可能,并找到了一个很好的解决方案。希望这会有所帮助:***.com/questions/9830216/… 【参考方案1】:您必须在Up
方法中使用自定义SQL脚本来设置默认值:
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 中)。
【讨论】:
谢谢。在迁移到 EF.Migrations 之前,我已经有了 SQL 脚本功能,所以这对我没有任何帮助。 我将此标记为答案。不是我希望的答案,但我认为情况就是这样。【参考方案2】:或者,如果您的实体从公共接口继承,您可以覆盖 DbContext 上的 SaveChanges 方法并在此时设置或更新属性(非常适合创建日期和上次更改日期)
【讨论】:
【参考方案3】:你可以使用
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 中的模型。答案建议您修改生成的脚本,以便在创建数据库时添加默认值。
您可以阅读有关实体框架代码优先迁移here 的更多信息。
【讨论】:
这段代码去哪儿了??什么是变量“c”?你能提供更多关于这行代码的上下文吗? @ThiagoSilva 正如您所建议的,在答案中添加了一些额外的上下文。 最简单的答案。【参考方案4】:我最近在 EF6 中遇到了这个问题(因为他们还没有修复它)。我发现无需手动修改 Migration 类的最简单方法是覆盖 Configuration 类中的 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();
希望对你有帮助
【讨论】:
我希望我可以为此向您授予特别代表赏金。我正在采用一个现有项目,该项目已经开始作为一个 nHibernate 项目,然后有人将其更改为 EF6。由于时间限制,他们没有加倍努力完成迁移的所有工作,而我现在打算这样做。这使得删除他们在手动数据库生成器中运行的几个一次性脚本成为可能。谢谢! @JonnySchnittger 覆盖 MigrationCodeGenerator 是个好主意。但是为什么不检索具有默认值([DefVal("getutcdate()")])的列(类字段)的属性/注释,而不是对所有列都这样做。可能吗?因为我在努力,但我做不到…… 我最终需要使用 SqlServerMigrationSqlGenerator,因为我使用了自动迁移,但我不需要做太多改变【参考方案5】:这是最简单的方法。
首先添加 DatabaseGeneratedOption.Computed
DataAnnotion
到您的财产
现在您可以修改 de SqlServerMigrationSqlGenarator
,覆盖 Genarate 方法并设置 DefaultValueSql = "GETDATE()" or "GETUTCDATE()";
【讨论】:
这个答案可能应该包括特定的自定义SqlServerMigrationSqlGenerator。 JonnySchnittger 的回答是粗略的,不包括对每个属性的属性注释或流畅的配置定义的支持。 (如果我最终实现这个而不是修改迁移,我会编辑它)【参考方案6】:改进:检查约束是否存在:
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");
【讨论】:
【参考方案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
命令的日期时间,对于新记录,它将设置创建日期时间
【讨论】:
【参考方案8】:在 .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");
【讨论】:
以上是关于可以使用实体框架迁移将 DateTime 字段默认为 GETDATE() 吗?的主要内容,如果未能解决你的问题,请参考以下文章
强制实体框架使用 datetime 而非 datetime2
我应该如何编写转换数据的实体框架迁移(最好使用 DbContext)?