如何使用ef core 2.1和pomelo创建createdon和UpdateOn

qlvxas9a  于 2021-06-21  发布在  Mysql
关注(0)|答案(1)|浏览(381)

在代码优先方法中,如何定义实体以便: CreatedOn not null—db在插入时使用当前时间戳生成值 Updated 空值由db使用当前时间戳更新时生成
示例实体:

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; }
}

数据库上下文:

public class MyContext : DbContext
{
    public MyContext(DbContextOptions options) : base(options) {}

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

数据库的最终结果应该是: CreatedOn not null-没有额外的-默认值可以是当前的\u时间戳 UpdatedOn null-额外更新当前\u时间戳-无默认值或默认值为null

0s7z1bwu

0s7z1bwu1#

问题:
我把范围缩小到(看起来)柚子里的虫子。问题在于:
https://github.com/pomelofoundation/pomelo.entityframeworkcore.mysql/issues/801
问题是,柚子创造了一个 defaultValue 的属性 DateTime 以及生成迁移时的其他结构。如果在迁移中设置了一个默认值,那么它将覆盖值生成策略,并且sql看起来不正确。
解决方法是生成迁移,然后手动修改迁移文件以设置 defaultValuenull (或删除整个行)。
例如,更改以下内容:

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

对此:

migrationBuilder.AddColumn<DateTime>(
                name: "UpdatedTime",
                table: "SomeTable",
                nullable: false)
                .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);

然后,迁移脚本将使用 DEFAULT CURRENT_TIMESTAMP 为了 TIMESTAMP . 如果你移除 [Column(TypeName = "TIMESTAMP")] 属性,它将使用 datetime(6) 列和吐出来 DEFAULT CURRENT_TIMESTAMP(6) .
解决方案:
我提出了一个解决方法,可以正确地实现created time(仅在insert时由数据库更新)和updated time(仅在insert和update时由数据库更新)。
首先,定义实体如下:

public class SomeEntity
{
    // Other properties here ...

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

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

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:

`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 始终为空。
这个 SetBeforeSaveBehavior 以及 SetAfterSaveBehavior 行阻止ef尝试用默认值覆盖创建的时间。它有效地使创建和更新的列从ef的Angular 来看是只读的,从而允许数据库完成所有的工作。
您甚至可以将其提取到接口和扩展方法中:

public interface ITimestampedEntity
    {
        DateTime CreatedTime { get; set; }
        DateTime UpdatedTime { get; set; }
    }
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;
}

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

public class SomeEntity : ITimestampedEntity
{
    // Other properties here ...

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

这允许您从内部设置实体 OnModelCreating() 像这样:

protected override void OnModelCreating(ModelBuilder builder)
{
    // Other model creating stuff here ...

    builder.Entity<SomeTimestampedEntity>().UseTimestampedProperty();
}

相关问题