問題:
私はこれをPomeloのバグ(と思われるもの)に絞り込みました。問題はここにあります:
https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues / 801
問題は、PomeloがdefaultValue
を作成することです DateTime
のプロパティ 移行を生成するときのその他の構造体。移行時にデフォルト値が設定されている場合、それは値生成戦略を上書きし、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);
移行スクリプトは、DEFAULT CURRENT_TIMESTAMP
を使用して正しいSQLを吐き出します。 TIMESTAMP
の場合 。 [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です。
SetBeforeSaveBehavior
およびSetAfterSaveBehavior
行は、EFが作成時刻をデフォルト値で上書きしようとするのを防ぎます。これにより、EFの観点から、作成済み列と更新済み列が効果的に読み取り専用になり、データベースがすべての作業を実行できるようになります。
これをインターフェースと拡張メソッドに抽出することもできます:
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();
}