Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Jak tworzyć CreatedOn i UpdatedOn za pomocą EF Core 2.1 i Pomelo

Problem:

Zawęziłem to do (co wydaje się być) błędem w Pomelo. Problem jest tutaj:

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

Problem polega na tym, że Pomelo tworzy defaultValue właściwość DateTime i inne struktury podczas generowania migracji. Jeśli w migracji ustawiona jest wartość domyślna, zastępuje ona strategię generowania wartości, a kod SQL wygląda wtedy niepoprawnie.

Rozwiązaniem jest wygenerowanie migracji, a następnie ręczne zmodyfikowanie pliku migracji, aby ustawić defaultValue na null (lub usuń całą linię).

Na przykład zmień to:

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

Do tego:

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

Skrypt migracji wyrzuci wtedy poprawny kod SQL z DEFAULT CURRENT_TIMESTAMP dla TIMESTAMP . Jeśli usuniesz [Column(TypeName = "TIMESTAMP")] atrybut, użyje datetime(6) kolumna i wypluć DEFAULT CURRENT_TIMESTAMP(6) .

ROZWIĄZANIE:

Wymyśliłem obejście, które poprawnie implementuje Utworzony czas (aktualizowany przez bazę danych tylko przy INSERT) i Zaktualizowany czas (aktualizowany przez bazę danych tylko przy INSERT i UPDATE).

Najpierw zdefiniuj swoją jednostkę w następujący sposób:

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

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

Następnie dodaj następujące elementy do 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);
}

Daje to idealną migrację początkową (gdzie migrationBuilder.CreateTable jest używany) i generuje oczekiwany kod 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),

To powinno pracuj również nad migracjami, które aktualizują istniejące tabele, ale upewnij się, że defaultValue jest zawsze pusta.

SetBeforeSaveBehavior i SetAfterSaveBehavior linie uniemożliwiają EF próbowanie zastąpienia czasu utworzenia wartością domyślną. Skutecznie sprawia, że ​​kolumny Utworzone i Zaktualizowane są odczytywane tylko z punktu widzenia EF, umożliwiając bazie danych wykonanie całej pracy.

Możesz nawet wyodrębnić to do interfejsu i metody rozszerzenia:

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

Następnie zaimplementuj interfejs we wszystkich swoich jednostkach oznaczonych znacznikiem czasu:

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

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

Pozwala to na skonfigurowanie encji z poziomu OnModelCreating() tak:

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

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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Python peewee save() nie działa zgodnie z oczekiwaniami

  2. Mysql wstawia losową datę i godzinę w podanym zakresie dat

  3. Zapytanie FULLTEXT z wynikami/rankingami w Postgresql

  4. Aktualizacja MySQL za pomocą PDO i przygotowana instrukcja nie działa

  5. baza danych latin-1 do utf-8