2015-11-09 8 views
18

現在、エンティティフレームワークのChangeTrackerを監査目的で使用しようとしています。私は私のDbContextのSaveChanges()メソッドをオーバーライドし、追加、変更、または削除されたエンティティのログを作成します。ここではそのFWIWためのコードは次のとおりです。動作を伴う多対多リレーションシップのエンティティフレームワークの変更の追跡

public override int SaveChanges() 
{ 
    var validStates = new EntityState[] { EntityState.Added, EntityState.Modified, EntityState.Deleted }; 
    var entities = ChangeTracker.Entries().Where(x => x.Entity is BaseEntity && validStates.Contains(x.State)); 
    var entriesToAudit = new Dictionary<object, EntityState>(); 
    foreach (var entity in entities) 
    { 
     entriesToAudit.Add(entity.Entity, entity.State); 
    } 
    //Save entries first so the IDs of new records will be populated 
    var result = base.SaveChanges(); 
    createAuditLogs(entriesToAudit, entityRelationshipsToAudit, changeUserId); 
    return result; 
} 

これは「通常」のエンティティのための素晴らしい作品。シンプルな多対多の関係のために、しかし、私は素晴らしいthisで説明したように「独立協会」を含めるには、この実装を拡張しなければならなかったSOので、同様のObjectContextを経由して、変更をアクセスする答え:

private static IEnumerable<EntityRelationship> GetRelationships(this DbContext context, EntityState relationshipState, Func<ObjectStateEntry, int, object> getValue) 
{ 
    context.ChangeTracker.DetectChanges(); 
    var objectContext = ((IObjectContextAdapter)context).ObjectContext; 

    return objectContext 
      .ObjectStateManager 
      .GetObjectStateEntries(relationshipState) 
      .Where(e => e.IsRelationship) 
      .Select(
       e => new EntityRelationship(
        e.EntitySet.Name, 
        objectContext.GetObjectByKey((EntityKey)getValue(e, 0)), 
        objectContext.GetObjectByKey((EntityKey)getValue(e, 1)))); 
} 
一度実装

、このでも機能しましたが、接合テーブルを使用する多対多リレーションシップの場合にのみ有効でした。これは、関係がクラス/エンティティではなく、各外部キーごとに2つの列を持つデータベーステーブルのみで表される状況を指しています。

ただし、データモデルに多対多リレーションシップがありますが、リレーションシップには「ビヘイビア」(プロパティ)があります。この例では、ProgramGroupPinプロパティを持つ多対多の関係である。このような状況で

public class Program 
{ 
    public int ProgramId { get; set; } 
    public List<ProgramGroup> ProgramGroups { get; set; } 
} 

public class Group 
{ 
    public int GroupId { get; set; } 
    public IList<ProgramGroup> ProgramGroups { get; set; } 
} 

public class ProgramGroup 
{ 
    public int ProgramGroupId { get; set; } 
    public int ProgramId { get; set; } 
    public int GroupId { get; set; } 
    public string Pin { get; set; } 
} 

、私はProgramGroupへの変更を見ていないよに(例えばピンが変更された場合。) 「通常の」DbContext ChangeTrackerも、ObjectContext関係メソッドも使用できません。ただし、コードをステップ実行すると、ObjectContextのStateEntriesに変更があることがわかりますが、エントリにはIsRelationship=falseがあります。もちろん、.Where(e => e.IsRelationship)の条件は失敗します。

私の質問は、それが実際のクラス/エンティティによって表され、なぜそれがObjectContext StateEntriesの関係としてマークされていないので、通常のDbContext ChangeTrackerに表示されない動作との多対多の関係ですか?また、これらのタイプの変更にアクセスするためのベストプラクティスは何ですか?

ありがとうございます。

EDIT:多分明示ProgramGroupの設定を定義しない場合、問題の原因であることをFrancescCastellsさんのコメント@に対応して 、私は次のような構成を追加しました:

public class ProgramGroupConfiguration : EntityTypeConfiguration<ProgramGroup> 
{ 
    public ProgramGroupConfiguration() 
    { 
     ToTable("ProgramGroups"); 
     HasKey(p => p.ProgramGroupId); 

     Property(p => p.ProgramGroupId).IsRequired(); 
     Property(p => p.ProgramId).IsRequired(); 
     Property(p => p.GroupId).IsRequired(); 
     Property(p => p.Pin).HasMaxLength(50).IsRequired(); 
    } 

そしてここでは、私の他の構成があります:

これらが実装されている
public class ProgramConfiguration : EntityTypeConfiguration<Program> 
{ 
    public ProgramConfiguration() 
    { 
     ToTable("Programs"); 
     HasKey(p => p.ProgramId); 
     Property(p => p.ProgramId).IsRequired(); 
     HasMany(p => p.ProgramGroups).WithRequired(p => p.Program).HasForeignKey(p => p.ProgramId); 
    } 
} 

public class GroupConfiguration : EntityTypeConfiguration<Group> 
{ 
    public GroupConfiguration() 
    { 
     ToTable("Groups"); 
     HasKey(p => p.GroupId); 
     Property(p => p.GroupId).IsRequired(); 
     HasMany(p => p.ProgramGroups).WithRequired(p => p.Group).HasForeignKey(p => p.GroupId); 
    } 

、EFはまだChangeTrackerで修正ProgramGroupは表示されません。

+0

プログラムグラフのマッピングを含めることはできますか? IsRelationship = falseの場合、おそらくrelantionship設定に何か問題がありますか? –

+0

@FrancescCastells私は実際に私のProgram-ProgramGroup-Group関係のための 'EntityTypeConfiguration'を明示的に定義していません。 EntityFrameworkは慣例によりそれを処理しています。 – im1dermike

+0

OK。それが正しくマップされていない限り、明示的にマップすることをお勧めします。たとえば、ナビゲーションプロパティのないGroupとの関係について実際に知っていますか? –

答えて

4

「の属性との関係」の概念は、実体関連モデリングの理論に記載されている一方で、限りEntity Frameworkのが関係しているとして、あなたのProgramGroupクラスが実体です。最初のコードスニペットのx.Entity is BaseEntityチェックを使用して、おそらく無意識のうちにフィルタリングしています。

1

私はこの問題は、あなたのProgramGroupクラスの定義およびオーバーライドされたSaveChanges方法にあると考えています。クラスの現在の定義では、EFは変更追跡プロキシを使用することができません。代わりに、EFは、SaveChangesメソッドの一部として行われるスナップショットの変更検出に依存しています。オーバーライドされたメソッドの最後にbase.SaveChanges()を呼び出したため、ChangeTrackerからリクエストしたときに変更がまだ検出されません。

SaveChangesメソッドの最初にChangeTracker.DetectChanges();を呼び出しても、変更追跡プロキシをサポートするようにクラスの定義を変更することもできます。変更追跡プロキシを作成するための

public class Program { 
    public int ProgramId { get; set; } 
    public virtual ICollection<ProgramGroup> ProgramGroups { get; set; } 
} 

public class Group { 
    public int GroupId { get; set; } 
    public virtual ICollection<ProgramGroup> ProgramGroups { get; set; } 
} 

基本的な要件は次のとおりです。

  • クラスはクラスが

  • クラス必見密封されてはならない公共

  • として宣言する必要があります抽象ではない

  • クラスには、パラメータを持たないパブリックまたは保護されたコンストラクタが必要です。

  • パブリック仮想のgetを持たなければならない関係の「多」側を表し、設定ナビゲーションプロパティは、アクセサの関係の「多」側を表しナビゲーションプロパティがICollection<T>

    として定義する必要があり

1

Entity Frameworkは、CSDLの結合テーブルのエンティティセットを持たないことによって多対多の関係を表し、代わりにマッピングによってこれを管理します。

注:エンティティフレームワーク参加の表は、あなたがこれでクーペには、ナビゲーションプロパティを自分で定義する必要がありますテーブル

両方のPKの以外の任意の列が含まれていない場合にのみ、多対多の関係をサポートしていますproplem。

これはlinkです。

関連する問題