Help us understand the problem. What is going on with this article?

[Entity Framework] メタデータ列(データ作成/更新日時など)の値設定

More than 1 year has passed since last update.

データ作成/更新日時などメタデータ列の値設定は共通処理にすると便利です。
Entity Framework ではどのタイミングでどのように設定すればよいでしょうか。

※データベースのトリガーにしないこと、ローカルのシステム日付を使用することの是非についてはここでは触れません。

方法はいくつかあります(一番きれいなのは最後の方式です)。

データバインドコントロールのイベント

たとえば ASP.NET Web フォームの場合、以下のハンドラで e.Values(Insert の場合)または e.NewValues(Update の場合)の各カラム要素に値を設定すると反映されます。

  • GridView.RowUpdating イベント
  • ListView.ItemInserting/ItemUpdating イベント
  • DetailsView.ItemInserting/ItemUpdating イベント
  • FormView.ItemInserting/ItemUpdating イベント

データソースコントロールのイベント

たとえば ASP.NET Web フォームの場合、以下のハンドラでエンティティを取得してプロパティに設定することができます。

ObjectDataSource.Inserting/Updating イベント

e.InputParameters[0] にエンティティが格納されています。

EntityDataSource.Inserting/Updating イベント

e.Entity にエンティティが格納されています。

SaveChanges メソッド(dynamic 方式)

個別のデータや操作に依存した値を設定するのには向きませんが、データ作成/更新日時の設定であれば、今回ご紹介した中で最も確実で実装効率のよい方法と言えます。
ここでは DbContext(EF 4.1 ~)の SaveChanges メソッドをオーバーライドして作成日時を設定する例をご紹介します。

DbContextの部分クラス
public partial class SampleEntities
{
    public override int SaveChanges()
    {
        SetCreatedDateTime();

        return base.SaveChanges();
    }

    private void SetCreatedDateTime()
    {
        DateTime now = DateTime.Now;

        // 追加エンティティのうち、CreatedDateTime プロパティを持つものを抽出
        var entities = this.ChangeTracker.Entries()
            .Where(e => e.State == EntityState.Added && e.CurrentValues.PropertyNames.Contains("CreatedDateTime"))
            .Select(e => e.Entity);

        foreach (dynamic entity in entities)
        {
            entity.CreatedDateTime = now;
        }
    }
}

SaveChanges メソッド(インターフェイス方式)

エンティティの部分クラス定義(インターフェイス実装)を手動で行う必要がありますが、dynamic 方式よりきれいです。
エンティティを追加したときなど、部分クラス定義を忘れないように注意が必要です(そのためのユニットテストが一番下にあります)。

IEntity.cs(エンティティのインターフェイス)
public interface IEntity
{
    int? CreatedUserId { get; set; }
    DateTime? CreatedDateTime { get; set; }
    int? UpdatedUserId { get; set; }
    DateTime? UpdatedDateTime { get; set; }
}
EntityPartials.cs(エンティティの部分クラス定義)
public partial class Foo : IEntity {}
public partial class Bar : IEntity {}
 : 
DbContextの部分クラス
public partial class SampleEntities
{
    public override int SaveChanges()
    {
        SetCreatedDateTime();

        return base.SaveChanges();
    }

    private void SetCreatedDateTime()
    {
        DateTime now = DateTime.Now;

        // 追加エンティティのうち、IEntity を実装したものを抽出
        var entities = this.ChangeTracker.Entries()
            .Where(e => e.State == EntityState.Added)
            .Select(e => e.Entity)
            .OfType<IEntity>();

        foreach (var entity in entities)
        {
            entity.CreatedDateTime = now;
        }
    }
}
IEntityの実装漏れを検出するユニットテスト
[TestMethod]
public void EntitiesShouldImplementIEntity()
{
    var entityTypes = typeof(SampleEntities)
        .GetProperties(BindingFlags.Public | BindingFlags.Instance)
        .Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
        .Select(p => p.PropertyType.GetGenericArguments().Single());

    foreach (var type in entityTypes)
    {
        if (type == typeof(Baz))
        {
            // 除外エンティティ
            continue;
        }

        Assert.IsTrue(typeof(IEntity).IsAssignableFrom(type), String.Format("{0} は IEntity を実装していません。", type.FullName));
    }
}
CodeOne
【品質と生産性にこだわるシステム開発】 .NET(C#/VB.NET)専門・リモート開発歴10年。即日・1時間から頼める常駐しないエンジニア。確かな技術で開発チームを手堅くサポートいたします。
https://codeone.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした