Masalah:
Saya telah mempersempit ini menjadi (yang tampaknya) bug di Pomelo. Masalah ada di sini:
https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues /801
Masalahnya adalah Pomelo membuat defaultValue
properti untuk DateTime
dan struct lainnya saat membuat migrasi. Jika nilai default disetel pada migrasi, itu menimpa strategi pembuatan nilai, dan SQL kemudian terlihat salah.
Solusinya adalah membuat migrasi, lalu memodifikasi file migrasi secara manual untuk menyetel defaultValue
ke null
(atau hapus seluruh baris).
Misalnya, ubah ini:
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);
Untuk ini:
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedTime",
table: "SomeTable",
nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);
Skrip migrasi kemudian akan mengeluarkan SQL yang benar dengan DEFAULT CURRENT_TIMESTAMP
untuk TIMESTAMP
. Jika Anda menghapus [Column(TypeName = "TIMESTAMP")]
atribut, itu akan menggunakan datetime(6)
kolom dan keluarkan DEFAULT CURRENT_TIMESTAMP(6)
.
SOLUSI:
Saya telah menemukan solusi yang mengimplementasikan Waktu yang Dibuat dengan benar (diperbarui oleh database hanya pada INSERT) dan Waktu yang diperbarui (diperbarui oleh database hanya pada INSERT dan UPDATE).
Pertama, tentukan entitas Anda seperti ini:
public class SomeEntity
{
// Other properties here ...
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
}
Kemudian, tambahkan berikut ini ke 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);
}
Ini menghasilkan migrasi awal yang sempurna (di mana migrationBuilder.CreateTable
digunakan), dan menghasilkan SQL yang diharapkan:
`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),
Ini harus juga bekerja pada migrasi yang memperbarui tabel yang ada, tetapi pastikan bahwa defaultValue
selalu nol.
SetBeforeSaveBehavior
dan SetAfterSaveBehavior
baris mencegah EF mencoba menimpa waktu yang Dibuat dengan nilai default. Ini secara efektif membuat kolom Created and Updated hanya membaca dari sudut pandang EF, memungkinkan database untuk melakukan semua pekerjaan.
Anda bahkan dapat mengekstrak ini menjadi antarmuka dan metode ekstensi:
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;
}
Kemudian implementasikan antarmuka pada semua entitas yang diberi cap waktu:
public class SomeEntity : ITimestampedEntity
{
// Other properties here ...
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
}
Ini memungkinkan Anda untuk mengatur Entitas dari dalam OnModelCreating()
seperti ini:
protected override void OnModelCreating(ModelBuilder builder)
{
// Other model creating stuff here ...
builder.Entity<SomeTimestampedEntity>().UseTimestampedProperty();
}