3

Entity Framework を使用して、SQL Server データベースの一時データを更新するための「汎用」メカニズムを実現しようとしています。

私がしたことは、ITemporalData存在する必要がある 2 つのプロパティを定義するDateTime ValidFromとという「マーカー」インターフェイスを作成することDateTime? ValidToです。

public interface ITemporalData
{
    DateTime ValidFrom { get; set; }
    DateTime? ValidTo { get; set; }
}

私はDbContext.SaveChanges()オーバーライドに「一般的な」アプローチを実装したいと考えていました。

  • 任意のオブジェクトのクローンITemporalDataを作成します。これにより、新しいオブジェクトが保存され ( EntityState.Added)、そのValidFrom値が現在の日付と時刻に設定されます。
  • 元の変更されたエントリをデータベース値にリセットし (.Reset()エンティティを呼び出して)、ValidToその「古い」レコードを現在の日付と時刻に設定します。

次のように、オーバーライドで変更されたITemporalDataオブジェクトを簡単に除外できます。SaveChanges()

public partial class MyDbContext
{
    // override the "SaveChanges" method
    public override int SaveChanges()
    {
        DateTime currentDateTime = DateTime.Now;

        // get the modified entities that implement the ITemporalData interface
        IEnumerable<DbEntityEntry<ITemporalData>> temporalEntities = ChangeTracker.Entries<ITemporalData>().Where(e => e.State == EntityState.Modified);

        foreach (var temporalEntity in temporalEntities)
        {
            // how would I do that, really? I only have an interface - can't clone an interface...... 
            var cloned = temporalEntity.Entity.Clone();

            // and once it's cloned, I would need to add the new record to the correct DbSet<T> to store it

            // set the "old" records "ValidTo" property to the current date&time
            temporalEntity.Entity.ValidTo = currentDateTime;
        }

        return base.SaveChanges();
    }
}

「変更されたレコードのクローンを作成する」アプローチに苦労しています-実際にはインターフェースしかありません-しかし、クローン作成(AutoMapperまたは他のアプローチを使用)は常に実際の基礎となる具体的なデータ型にITemporalData依存します.....

4

2 に答える 2

2

エンティティのクローンを作成するには、リフレクション ( ) を介して新しいインスタンスを作成し、リフレクションを介してActivator.CreateInstanceすべてのプリミティブ (非ナビゲーション) プロパティをそれにコピーします。自動マッパー ツールはナビゲーション プロパティにもアクセスするため、遅延読み込みが発生する可能性があるため (または少なくとも遅延読み込みが無効になっていることを確認してください)、自動マッパー ツールを使用しないことをお勧めします。

リフレクションが気に入らない場合 (自動マッパーはそれを使用することに注意してください) - インターフェイスを継承し、すべてのエンティティに対してメソッドをICloneable実装することもできます (エンティティが自動生成される場合は、部分クラスを使用します)。次に、各エンティティは、反映することなく、クローンの作成方法を決定します。この方法は、クローン ロジックが複雑な場合にも利点があります (たとえば、ナビゲーション プロパティから関連オブジェクトをクローンする場合など)。CloneITemporalData

エンティティを正しい DbSet に追加するには、次の型指定されていないSetメソッドを使用しDbContextます。

this.Set(temporalEntity.GetType()).Add(temporalEntity);
于 2016-05-31T20:06:10.470 に答える
1

このCloneメソッドをコンテキストに追加できます。

T Clone<T>(DbEntityEntry<T> entry)
    where T : class
{
    var proxyCreationEnabled = this.Configuration.ProxyCreationEnabled;
    try
    {
        this.Configuration.ProxyCreationEnabled = false;
        var clone = (T)entry.CurrentValues.ToObject();
        Set(clone.GetType()).Add(clone);
        return clone;
    }
    finally
    {
        this.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
    }
}

そして、次のように使用します。

var cloned = Clone(temporalEntity);

clone.GetTypeはクローン オブジェクトの実際の型を返しますTが、 はコンパイル時の型になりITemporalDataます。

これは、EF 独自のインフラストラクチャを使用してクローンを作成します。これは、間違いなくリフレクションよりも高速です。

クローンの状態はすぐに に設定されAddedますが、遅延読み込みは実行されません。ただし、クローンがプロキシにならないようにする方が安全な場合があります。したがって、クローンで他のことを行うことにした場合に備えて、遅延読み込みがトリガーされることはありません。(Evk の鋭いコメントに感謝します)。

于 2016-05-31T21:29:36.747 に答える