sql >> Databasteknik >  >> RDS >> Mysql

Hur man skapar CreatedOn och UpdatedOn med EF Core 2.1 och Pomelo

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();
}



  1. HikariCP Postgresql Driver hävdar att de inte accepterar JDBC URL

  2. Lägg till månader till ett datum i SQLite

  3. Big Data med PostgreSQL och Apache Spark

  4. emulerade förberedda uttalanden vs verkliga förberedda uttalanden