koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

Entity Framework Core 2.0 で CreatedAt UpdatedAt

自分はORMではS2JDBCの洗礼を浴びていて、静的な型がついているEntityをRDBにMappingしてくれるORMが好きだ。

最近ではEntity Framework Coreが良くて、ちょっと前から使っているシステムがある。

唐突にそれでやっている工夫を書きたくなったので書く。

タイトルの通りあるEntityの生成時刻と更新時刻をアプリケーションプログラムで付与するものだ。

確認してみたらバージョンは 2.0.x だった。2つほどマイナーバージョンを置いて行かれている。大分変っているのだろうなぁ。

それはさておき。

まず、生成・更新時刻を付与したいEntityは以下の様なInterfaceを実装しておく。

    public interface ITrackableEntity
    {
        DateTime UpdatedAt { get; set; }
        DateTime CreatedAt { get; set; }
    }

例えばこんな感じ。

    public class BlobImage : ITrackableEntity
    {
        public int BlobImageId { get; set; }
        public string Path { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime UpdatedAt { get; set; }
    }

後は、DBコンテキストのセーブ手前で変更があった ITrackableEntity に現在の時刻を設定してやる。

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
        }

        public override int SaveChanges()
        {
            AddTimestamps();
            return base.SaveChanges();
        }

        public override int SaveChanges(bool acceptAllChangesOnSuccess)
        {
            AddTimestamps();
            return base.SaveChanges(acceptAllChangesOnSuccess);
        }

        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            AddTimestamps();
            return await base.SaveChangesAsync(cancellationToken);
        }

        public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
        {
            AddTimestamps();
            return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
        }

        private void AddTimestamps()
        {
            DateTime time = DateTime.Now;

            var entries = ChangeTracker.Entries()
                .Where(entry => entry.Entity is ITrackableEntity)
                .ToList();

            foreach (var entry in entries.Where(e => e.State == EntityState.Added))
            {
                entry.Property("CreatedAt").CurrentValue = time;
                entry.Property("UpdatedAt").CurrentValue = time;
            }

            foreach (var entry in entries.Where(e => e.State == EntityState.Modified))
            {
                entry.Property("CreatedAt").IsModified = false;
                entry.Property("UpdatedAt").CurrentValue = time;
            }
        }

        public DbSet<BlobImage> BlobImage { get; set; }
    }

私はこれでEntity Framework Core 2.0.x で CreatedAt, UpdatedAt を設定しました。

他にもやり方はあると思います。