sql >> データベース >  >> RDS >> Mysql

EFCore2.1とPomeloを使用してCreatedOnとUpdatedOnを作成する方法

    問題:

    私はこれをPomeloのバグ(と思われるもの)に絞り込みました。問題はここにあります:

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

    問題は、PomeloがdefaultValueを作成することです DateTimeのプロパティ 移行を生成するときのその他の構造体。移行時にデフォルト値が設定されている場合、それは値生成戦略を上書きし、SQLは正しくないように見えます。

    回避策は、移行を生成してから、移行ファイルを手動で変更してdefaultValueを設定することです。 nullに (または行全体を削除します)。

    たとえば、次のように変更します。

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

    これに:

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

    移行スクリプトは、DEFAULT CURRENT_TIMESTAMPを使用して正しいSQLを吐き出します。 TIMESTAMPの場合 。 [Column(TypeName = "TIMESTAMP")]を削除した場合 属性の場合、datetime(6)を使用します 列を作成し、DEFAULT CURRENT_TIMESTAMP(6)を吐き出します。 。

    解決策:

    作成時間(データベースによってINSERTでのみ更新される)と更新時間(データベースによってINSERTとUPDATEでのみ更新される)を正しく実装する回避策を考え出しました。

    まず、エンティティを次のように定義します。

    public class SomeEntity
    {
        // Other properties here ...
    
        public DateTime CreatedTime { get; set; }
        public DateTime UpdatedTime { get; set; }
    }
    

    次に、以下を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);
    }
    

    これにより、完全な初期移行が生成されます(migrationBuilder.CreateTable が使用されます)、期待される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),
    

    これはすべき 既存のテーブルを更新する移行でも機能しますが、defaultValueであることを確認してください 常にnullです。

    SetBeforeSaveBehavior およびSetAfterSaveBehavior 行は、EFが作成時刻をデフォルト値で上書きしようとするのを防ぎます。これにより、EFの観点から、作成済み列と更新済み列が効果的に読み取り専用になり、データベースがすべての作業を実行できるようになります。

    これをインターフェースと拡張メソッドに抽出することもできます:

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

    次に、タイムスタンプ付きのすべてのエンティティにインターフェースを実装します。

    public class SomeEntity : ITimestampedEntity
    {
        // Other properties here ...
    
        public DateTime CreatedTime { get; set; }
        public DateTime UpdatedTime { get; set; }
    }
    

    これにより、OnModelCreating()内からエンティティを設定できます。 そのように:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        // Other model creating stuff here ...
    
        builder.Entity<SomeTimestampedEntity>().UseTimestampedProperty();
    }
    



    1. 文字列を正しい方法で分割する–または次善の方法

    2. 1つのSQLに複数のWITHASを含めることはできますか-OracleSQL

    3. Laravel5.6withCountおよびwhereステートメント

    4. 単一のSQLステートメントを使用して1つのテーブルから別のテーブルにレコードを移動することは可能ですか?