2013-03-07 12 views
17

この特定のエラーメッセージに関するいくつかの質問/回答を読んだことがありますが、2つのオブジェクト間の関係は、異なるObjectContextオブジェクトにアタッチされているため、定義できません。

私は何度もEF4コンテキストを作成して使用してから処分する必要があります。私のアプリケーションでは、さまざまなコンテキストオブジェクトを使用して、ここではエンティティをロードしていますが、最終的にエンティティを関連付けることを望んでいます。

簡単にエラーが発生する単純なコンソールアプリケーションを作成しました。非常に単純なモデルが図式化され、コードが続きます。

同じコンテキストを共有するために2つの異なるエンティティを取得するにはどうすればよいですか?私は実際に新しいコンテキストを作成し、2つのエンティティを再度ロードする必要がありますか(既にそれらを持っていても)、単純にそれらを関連付けて保存しますか?

すでに存在する適切な質問/回答が欠けていた場合は、私に正しい場所を指摘してください。

Simple EF4 Model diagram

internal class Program { 

    private static void Main(string[] args) { 
     DeleteAllEntities(); 
     CreateInitialEntities(); 
     Owner o = LoadOwner(); 
     Child c = LoadChild(); 
     AssociateAndSave(o, c); 
    } 

    private static void AssociateAndSave(Owner o, Child c) { 
     using (var context = new ModelEntities()) { 
      // Exception occurs on the following line. 
      o.Children.Add(c); 
      context.Attach(o); 
      context.SaveChanges(); 
     } 
    } 

    private static Owner LoadOwner() { 
     using (var context = new ModelEntities()) { 
      return ((from o in context.Owners 
        select o).First()); 
     } 
    } 

    private static Child LoadChild() { 
     using (var context = new ModelEntities()) { 
      return ((from c in context.Children 
        select c).First()); 
     } 
    } 

    private static void CreateInitialEntities() { 
     using (var context = new ModelEntities()) { 
      Owner owner = new Owner(); 
      Child child = new Child(); 
      context.Owners.AddObject(owner); 
      context.Children.AddObject(child); 
      context.SaveChanges(); 
     } 
    } 

    private static void DeleteAllEntities() { 
     using (var context = new ModelEntities()) { 
      List<Child> children = (from c in context.Children 
            select c).ToList(); 
      foreach (var c in children) 
       context.Children.DeleteObject(c); 
      List<Owner> owners = (from o in context.Owners 
            select o).ToList(); 
      foreach (var o in owners) 
       context.Owners.DeleteObject(o); 
      context.SaveChanges(); 
     } 
    } 
} 
+1

Hmm ..単に 'context.Attach(o);を呼び出します。 context.Attach(c); 'はエラーを防ぎます。しかし、私が実際のアプリケーションでこれを試したところ、 'ObjectStateManagerに同じキーを持つオブジェクトがすでに存在しています。エンティティがこの例と同様の方法でロードされていても、ObjectStateManagerは同じキーを持つ複数のオブジェクトを追跡できません。 – Steve

答えて

15

同じコンテキストで子オブジェクトと所有者オブジェクトへの参照を取得する必要があります。このためのアプローチは、IDを取得して、それらをパラメータとしてAssociateAndSave(int oId, int cId)メソッドに渡すことです。

次に、同じコンテキストのオブジェクトへの参照を取得し、添付ファイルを作成します。

private static void Main(string[] args) 
{ 
    DeleteAllEntities(); 
    CreateInitialEntities(); 
    int oId = LoadOwnerId(); 
    int cId = LoadChildId(); 
    AssociateAndSave(oId, cId); 
} 

private static int LoadOwnerId() 
{ 
    using (var context = new ModelEntities()) 
    { 
     return (from o in context.Owners 
       select o).First().Id; 
    } 
} 

private static int LoadChildId() 
{ 
    using (var context = new ModelEntities()) 
    { 
     return (from c in context.Children 
       select c).First().Id; 
    } 
} 

private static void AssociateAndSave(int oId, int cId) 
{ 
    using (var context = new ModelEntities()) 
    { 
     var owner = (from o in context.Owners 
         select o).FirstOrDefault(o => o.ID == oId); 
     var child = (from o in context.Children 
         select o).FirstOrDefault(c => c.ID == cId); 

     owner.Children.Add(child); 
     context.Attach(owner); 
     context.SaveChanges(); 
    } 
} 
+1

これは良い解決策ですが、EF 4.0では機能しません。 – Freeman

+2

実際にこれをやり始めましたが、データベースから以前にデータベースから読み込んだエンティティのデータベースをクエリするのは少し無駄でした。 – Steve

+1

私はこの質問に両方の答えの部分を持っていくことに決めましたが、残念ながら答えとして両方をマークすることはできません。:)これを見つけた人は誰でもhttp://stackoverflow.com/a/5695009/425871 。 – Steve

3

各コンテキストは、個別にそのオブジェクトを追跡するので、あなたは、別のコンテキストのインスタンスと、1つのコンテキストからオブジェクトを削除することはできません。

あなたの操作が同じコンテキスト内で行われるように、それぞれのメソッドにコンテキストタイプのパラメータを提供することが理想です。

EX:それはORMのに非常に有用であるとして

private static void CrudOperationExample(DBContext contextInstane) 
{ 
    //perform crud operations. 
} 

私はあなたがもっとうまくそれをしたい、私は、あなたが依存性注入のために見てください。

+1

私はこれを行うことができるようにコンテキストを生かしていましたが、何度も何度も必要なときに非常に短い時間だけコンテキストを保持してください。私のアプリケーションでは、コンテキストを5分、あるいは1時間にすることも可能です。ユーザーがウィンドウを開いたままにしておいた期間に依存します。 – Steve

+1

また、各コンテキストがそのエンティティを別々に追跡しているが、コンテキストが破棄されている(すべての操作が 'using'ブロックにあります)。 – Steve

関連する問題