Probleem:
Ik heb dit teruggebracht tot (wat lijkt op) een bug in Pomelo. Probleem is hier:
https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues /801
Het probleem is dat Pomelo een defaultValue
. maakt eigenschap voor DateTime
en andere structuren bij het genereren van de migratie. Als er een standaardwaarde is ingesteld voor de migratie, wordt de strategie voor het genereren van waarden overschreven en ziet de SQL er dan onjuist uit.
De tijdelijke oplossing is om de migratie te genereren en vervolgens het migratiebestand handmatig aan te passen om de defaultValue
in te stellen. naar null
(of verwijder de hele regel).
Verander dit bijvoorbeeld:
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);
Hierop:
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedTime",
table: "SomeTable",
nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);
Het migratiescript zal dan de juiste SQL uitspugen met DEFAULT CURRENT_TIMESTAMP
voor TIMESTAMP
. Als u de [Column(TypeName = "TIMESTAMP")]
. verwijdert attribuut, zal het een datetime(6)
. gebruiken kolom en spuug DEFAULT CURRENT_TIMESTAMP(6)
. uit .
OPLOSSING:
Ik heb een tijdelijke oplossing bedacht die Create Time (alleen bijgewerkt door de database op INSERT) en Updated time (alleen bijgewerkt door de database op INSERT en UPDATE) correct implementeert.
Definieer eerst uw entiteit als volgt:
public class SomeEntity
{
// Other properties here ...
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
}
Voeg vervolgens het volgende toe aan 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);
}
Dit levert een perfecte initiële migratie op (waar migrationBuilder.CreateTable
wordt gebruikt), en genereert de verwachte 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),
Dit moet werk ook aan migraties die bestaande tabellen bijwerken, maar zorg ervoor dat defaultValue
is altijd nul.
Het SetBeforeSaveBehavior
en SetAfterSaveBehavior
regels voorkomen dat EF ooit probeert de Gemaakte tijd te overschrijven met een standaardwaarde. Het zorgt ervoor dat de kolommen Aangemaakt en Bijgewerkt alleen lezen vanuit het oogpunt van EF, waardoor de database al het werk kan doen.
Je kunt dit zelfs extraheren in een interface en extensiemethode:
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;
}
Implementeer vervolgens de interface op al uw entiteiten met tijdstempel:
public class SomeEntity : ITimestampedEntity
{
// Other properties here ...
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
}
Hiermee kunt u de entiteit instellen vanuit OnModelCreating()
zoals zo:
protected override void OnModelCreating(ModelBuilder builder)
{
// Other model creating stuff here ...
builder.Entity<SomeTimestampedEntity>().UseTimestampedProperty();
}