首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用EFCore2.1和Pomelo创建CreatedOn和UpdatedOn

如何使用EFCore2.1和Pomelo创建CreatedOn和UpdatedOn
EN

Stack Overflow用户
提问于 2018-06-12 18:43:20
回答 1查看 2.8K关注 0票数 5

在代码优先方法中,如何定义我的实体,以便:

  • CreatedOn非空值是由带有当前时间戳的db在插入时生成的。
  • Updated空值是由带有当前时间戳的db更新时生成的。

抽样实体:

代码语言:javascript
复制
public class MyEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Column(TypeName = "TIMESTAMP")]
    public DateTime CreatedOn { get; set; }
    [Column(TypeName = "TIMESTAMP")]
    public DateTime UpdatedOn { get; set; }
}

DbContext:

代码语言:javascript
复制
public class MyContext : DbContext
{
    public MyContext(DbContextOptions options) : base(options) {}

    public DbSet<MyEntity> Entities { get; set; }
}

数据库的最终结果应该是:

  • CreatedOn不为空-没有额外的默认值可以是CURRENT_TIMESTAMP。
  • UpdatedOn NULL -在update CURRENT_TIMESTAMP上额外-没有默认或默认为NULL
EN

回答 1

Stack Overflow用户

发布于 2019-07-16 06:53:18

发行:

我把它缩小到了(似乎是) Pomelo的一个bug。问题在此:

https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/801

问题是,在生成迁移时,Pomelo为DateTime和其他结构创建了一个DateTime属性。如果在迁移中设置了默认值,则会重写值生成策略,然后SQL看起来不正确。

解决方法是生成迁移,然后手动修改迁移文件,将defaultValue设置为null (或删除整行)。

例如,更改以下内容:

代码语言:javascript
复制
migrationBuilder.AddColumn<DateTime>(
                name: "UpdatedTime",
                table: "SomeTable",
                nullable: false,
                defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)))
                .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);

对此:

代码语言:javascript
复制
migrationBuilder.AddColumn<DateTime>(
                name: "UpdatedTime",
                table: "SomeTable",
                nullable: false)
                .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);

然后,迁移脚本将使用DEFAULT CURRENT_TIMESTAMPTIMESTAMP提供正确的SQL。如果删除[Column(TypeName = "TIMESTAMP")]属性,它将使用一个datetime(6)列并输出DEFAULT CURRENT_TIMESTAMP(6)

解决方案:

我想出了一个解决方案,它正确地实现了创建时间(由数据库只在INSERT上更新)和更新时间(仅在INSERT和UPDATE上由数据库更新)。

首先,像这样定义您的实体:

代码语言:javascript
复制
public class SomeEntity
{
    // Other properties here ...

    public DateTime CreatedTime { get; set; }
    public DateTime UpdatedTime { get; set; }
}

然后,将以下内容添加到OnModelCreating()

代码语言:javascript
复制
protected override void OnModelCreating(ModelBuilder builder)
{
    // Other model creating stuff here ...

    builder.Entity<SomeEntity>.Property(d => d.CreatedTime).ValueGeneratedOnAdd();
    builder.Entity<SomeEntity>.Property(d => d.UpdatedTime).ValueGeneratedOnAddOrUpdate();

    builder.Entity<SomeEntity>.Property(d => d.CreatedTime).Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
    builder.Entity<SomeEntity>.Property(d => d.CreatedTime).Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
    builder.Entity<SomeEntity>.Property(d => d.UpdatedTime).Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
    builder.Entity<SomeEntity>.Property(d => d.UpdatedTime).Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
}

这将产生一个完美的初始迁移(在其中使用migrationBuilder.CreateTable ),并生成预期的SQL:

代码语言:javascript
复制
`created_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),

这也应该适用于更新现有表的迁移,但一定要确保defaultValue始终为null。

SetBeforeSaveBehaviorSetAfterSaveBehavior行防止EF试图用默认值覆盖创建的时间。它有效地使创建的和更新的列只从EF的角度读取,从而允许数据库完成所有的工作。

您甚至可以将其解压缩到接口和扩展方法中:

代码语言:javascript
复制
public interface ITimestampedEntity
    {
        DateTime CreatedTime { get; set; }
        DateTime UpdatedTime { get; set; }
    }
代码语言:javascript
复制
public static EntityTypeBuilder<TEntity> UseTimestampedProperty<TEntity>(this EntityTypeBuilder<TEntity> entity) where TEntity : class, ITimestampedEntity
{
    entity.Property(d => d.CreatedTime).ValueGeneratedOnAdd();
    entity.Property(d => d.UpdatedTime).ValueGeneratedOnAddOrUpdate();

    entity.Property(d => d.CreatedTime).SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
    entity.Property(d => d.CreatedTime).SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
    entity.Property(d => d.UpdatedTime).SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
    entity.Property(d => d.UpdatedTime).SetAfterSaveBehavior(PropertySaveBehavior.Ignore);

    return entity;
}

然后在所有时间戳实体上实现接口:

代码语言:javascript
复制
public class SomeEntity : ITimestampedEntity
{
    // Other properties here ...

    public DateTime CreatedTime { get; set; }
    public DateTime UpdatedTime { get; set; }
}

这允许您从OnModelCreating()内部设置实体,如下所示:

代码语言:javascript
复制
protected override void OnModelCreating(ModelBuilder builder)
{
    // Other model creating stuff here ...

    builder.Entity<SomeTimestampedEntity>().UseTimestampedProperty();
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50823809

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档