Проблем:
Стесних това до (това, което изглежда) грешка в Pomelo. Проблемът е тук:
https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MyesSq /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);
След това скриптът за миграция ще изплюе правилния SQL с DEFAULT CURRENT_TIMESTAMP
за 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
винаги е нула.
SetBeforeSaveBehavior
и SetAfterSaveBehavior
редовете не позволяват на EF някога да се опита да презапише времето за създаване със стойност по подразбиране. Той ефективно прави колоните Created и Updated да се четат само от гледна точка на 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();
}