Problem:
Jag har begränsat detta till (vad som verkar vara) en bugg i Pomelo. Problemet är här:
https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues /801
Problemet är att Pomelo skapar ett defaultValue
egenskap för DateTime
och andra strukturer när migreringen genereras. Om ett standardvärde ställs in på migreringen åsidosätter det värdegenereringsstrategin, och SQL:n ser då felaktig ut.
Lösningen är att generera migreringen och sedan manuellt ändra migreringsfilen för att ställa in defaultValue
till null
(eller ta bort hela raden).
Ändra till exempel detta:
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);
Till detta:
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedTime",
table: "SomeTable",
nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);
Migreringsskriptet kommer sedan att spy ut rätt SQL med DEFAULT CURRENT_TIMESTAMP
för TIMESTAMP
. Om du tar bort [Column(TypeName = "TIMESTAMP")]
attribut, kommer den att använda en datetime(6)
kolumn och spotta ut DEFAULT CURRENT_TIMESTAMP(6)
.
LÖSNING:
Jag har kommit på en lösning som korrekt implementerar Created Time (uppdaterad av databasen endast på INSERT) och Uppdaterad tid (uppdaterad av databasen endast på INSERT och UPDATE).
Först definierar du din enhet så här:
public class SomeEntity
{
// Other properties here ...
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
}
Lägg sedan till följande i 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);
}
Detta ger en perfekt initial migrering (där migrationBuilder.CreateTable
används), och genererar förväntad 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),
Detta bör fungerar även med migrering som uppdaterar befintliga tabeller, men se till att defaultValue
är alltid null.
SetBeforeSaveBehavior
och SetAfterSaveBehavior
linjer hindrar EF från att någonsin försöka skriva över den skapade tiden med ett standardvärde. Det gör att kolumnerna Skapat och Uppdaterat endast läses från EF:s synvinkel, vilket gör att databasen kan göra allt arbete.
Du kan till och med extrahera detta i ett gränssnitt och en tilläggsmetod:
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;
}
Implementera sedan gränssnittet på alla dina tidsstämplade enheter:
public class SomeEntity : ITimestampedEntity
{
// Other properties here ...
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
}
Detta låter dig ställa in enheten från OnModelCreating()
som så:
protected override void OnModelCreating(ModelBuilder builder)
{
// Other model creating stuff here ...
builder.Entity<SomeTimestampedEntity>().UseTimestampedProperty();
}