gpt4 book ai didi

c# - 如何使用 EF Core 2.1 和 Pomelo 创建 CreatedOn 和 UpdatedOn

转载 作者:可可西里 更新时间:2023-11-01 07:43:15 24 4
gpt4 key购买 nike

在 Code First 方法中,如何定义我的实体以便:

  • CreatedOn NOT NULL - 值由具有当前时间戳的数据库在插入时生成
  • 已更新 NULL - 值是在数据库更新时使用当前时间戳生成的

示例实体:

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 - 没有额外的 - 默认值可以是 CURRENT_TIMESTAMP
  • UpdatedOn NULL - 额外更新 CURRENT_TIMESTAMP - 无默认值或默认值为 NULL

最佳答案

问题:

我已将其缩小为(看起来是)Pomelo 中的错误。问题在这里:

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

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

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

例如,改变这个:

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

然后,迁移脚本将为 TIMESTAMP 生成带有 DEFAULT CURRENT_TIMESTAMP 的正确 SQL。如果删除 [Column(TypeName = "TIMESTAMP")] 属性,它将使用 datetime(6) 列并吐出 DEFAULT CURRENT_TIMESTAMP(6)

解决方案:

我想出了一个解决方法,可以正确实现创建时间(仅在 INSERT 时由数据库更新)和更新时间(仅在 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 始终为 null。

SetBeforeSaveBehaviorSetAfterSaveBehavior 行阻止 EF 尝试使用默认值覆盖创建时间。从 EF 的角度来看,它有效地使 Created 和 Updated 列只读,从而允许数据库完成所有工作。

您甚至可以将其提取到接口(interface)和扩展方法中:

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

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

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

关于c# - 如何使用 EF Core 2.1 和 Pomelo 创建 CreatedOn 和 UpdatedOn,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50823809/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com