sql >> Database teknologi >  >> RDS >> Mysql

Sådan opretter du CreatedOn og UpdatedOn ved hjælp af EF Core 2.1 og Pomelo

Problem:

Jeg har indsnævret dette til (hvad der ser ud til at være) en fejl i Pomelo. Udgaven er her:

https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues /801

Problemet er, at Pomelo opretter en defaultValue egenskab for DateTime og andre strukturer, når migreringen genereres. Hvis der er angivet en standardværdi på migreringen, tilsidesætter den værdigenereringsstrategien, og SQL'en ser derefter forkert ud.

Løsningen er at generere migreringen og derefter manuelt ændre migreringsfilen for at indstille defaultValue til null (eller fjern hele linjen).

For eksempel, ændre dette:

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

Til dette:

migrationBuilder.AddColumn<DateTime>(
                name: "UpdatedTime",
                table: "SomeTable",
                nullable: false)
                .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);

Migreringsscriptet vil derefter spytte den korrekte SQL ud med DEFAULT CURRENT_TIMESTAMP for TIMESTAMP . Hvis du fjerner [Column(TypeName = "TIMESTAMP")] attribut, vil den bruge en datetime(6) kolonne og spyt DEFAULT CURRENT_TIMESTAMP(6) ud .

LØSNING:

Jeg har fundet en løsning, der implementerer oprettet tid (kun opdateret af databasen på INSERT) og opdateret tid (kun opdateret af databasen på INSERT og UPDATE) korrekt.

Først skal du definere din enhed sådan:

public class SomeEntity
{
    // Other properties here ...

    public DateTime CreatedTime { get; set; }
    public DateTime UpdatedTime { get; set; }
}

Tilføj derefter følgende til 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);
}

Dette giver en perfekt indledende migrering (hvor migrationBuilder.CreateTable bruges), og genererer den forventede 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),

Dette skal arbejder også på migreringer, der opdaterer eksisterende tabeller, men sørg for, at defaultValue er altid nul.

SetBeforeSaveBehavior og SetAfterSaveBehavior linjer forhindrer EF i nogensinde at forsøge at overskrive det oprettede tidspunkt med en standardværdi. Det gør effektivt, at kolonnerne Oprettet og Opdateret kun kan læses fra EF's synspunkt, hvilket gør det muligt for databasen at udføre alt arbejdet.

Du kan endda udtrække dette i en grænseflade og udvidelsesmetode:

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

Implementer derefter grænsefladen på alle dine tidsstemplede enheder:

public class SomeEntity : ITimestampedEntity
{
    // Other properties here ...

    public DateTime CreatedTime { get; set; }
    public DateTime UpdatedTime { get; set; }
}

Dette giver dig mulighed for at opsætte entiteten inde fra OnModelCreating() sådan:

protected override void OnModelCreating(ModelBuilder builder)
{
    // Other model creating stuff here ...

    builder.Entity<SomeTimestampedEntity>().UseTimestampedProperty();
}



  1. Sådan viser du alle lokaliteter i MariaDB

  2. MySQL 8.0 - Klienten understøtter ikke godkendelsesprotokol anmodet af serveren; overveje at opgradere MySQL-klienten

  3. Et injektionsangreb, der lykkes med mysql_query, men mislykkes med mysqli_query

  4. mysql skærer