2011-07-11 9 views
1

システム内には、すべて基本クラスEntityから継承したオブジェクトが多数あります。これらのオブジェクトは、1対1、1対多、多対多など多くの関係を持っています。私はWCFを使用しているので、私のDbContextオブジェクトはすべてのCRUD呼び出しで切断されます。これにより、関係のあるオブジェクトに対する基本的なCRUD操作に関するいくつかの問題が発生しました。エンティティフレームワーク:複雑な関係を持つオブジェクトのCRUD

たとえば、基本的な親子関係を持つオブジェクトがあります。私はノードと呼ぶ。

[DataContract(IsReference=true)] 
public partial class Node : Entity 
{ 

    [DataMember] 
    public long ID { get; private set; } 

    [DataMember] 
    public long? ParentID { get; set; } 

    [DataMember] 
    public List<Node> Children { get; set; } 

} 

は、私はすでに存在しても、まだ存在していない子を持つノードを追加するには、子を持つ新しいノードを追加できるようにしたいです。

// Node with a new child node 
Node nodeWithNewChild = new Node() 
{ 
    Children = new List<Node>() 
    { 
    new Node() 
    } 
} 

// A pre-existing child node 
Node existingChildNode = new Node(); 

// Node with a pre-existing child node 
Node nodeWithExistingChild = new Node() 
{ 
    Children = new List<Node>() 
    { 
    existingChildNode 
    } 
} 

問題は、どのように私がそれを行っても、Entity Frameworkは混乱します。

私は基本的なDbContext.Nodes.Addの操作を使用すると、テストケースで既存の子を駄目です。データベース内の既存の子を重複して入力し、この新しい子に正しいParentIDを与えます。これは、子供をループして、最初に子どもにDbContext.Nodes.Addを使用すると発生します。

すべての子をループして、すべての子にDbContext.Nodes.Attachを使用してからDbContext.Nodes.Addを親に使用しましたが、これは新しい子供のテストケースで例外を発生させます。

System.Data.Entity.Infrastructure.DbUpdateConcurrencyException:ストア 更新、挿入、または削除ステートメントが 行(0)の予想外の数に影響を与えました。エンティティ がロードされたため、エンティティが変更または削除されている可能性があります。 ObjectStateManagerエントリを更新します。 ...

たとえば、子を持つ子を持つノードを子などに追加した場合など、これがどうなるか心配です。 CRUDメソッドがすべての有効なオブジェクト構造に適切に反応するようにしたい。

私が見つけることができたことから、EFはこの種のものには意味がない、あるいは悪いことがあります。自分で関係を管理することが最善です。これは本当ですか?もしそうなら、私はそれに従うことができる例はありますか?任意のヒントなど?

私はリフレクションを使用する関係を自分自身で処理する方法を使い始めましたが、基本的な問題であるはずのものを解決するばかげた方法だと思います。

答えて

1

既存の子ノードには接続し、新しい子ノードには接続しないでください。これは次のようになります(既存のID> 0手段):あなたは自分のIDを見ることで、新規および既存のノードを区別することができましょう

childNodeも挿入されます0ケースID ==で
if (childNode.ID > 0) 
    context.Nodes.Attach(childNode); 

Node newParentNode = new Node() 
{ 
    Children = new List<Node>() 
    { 
     childNode 
    } 
}; 

context.Nodes.Add(newParentNode); 
context.SaveChanges(); 

DB。それ以外の場合(コンテキストへのアタッチにより)、新しい子ノードレコードは作成されません。

編集:

あなたは以下のことによって、上記のコードでは2つの最初の行を置き換えることができ、既存および新規のノードを混在させることができ、子供や孫などの複雑なグラフを使用している場合:

AttachOrAddChildren(childNode); 

そして、次のメソッドを追加します

void AttachOrAddChildren(Node node) 
{ 
    if (node.Children != null) 
    { 
     foreach(var child in node.Children) 
     { 
      if (child.ID > 0) 
       context.Nodes.Attach(child); 
      else 
       context.Nodes.Add(child); 
      AttachOrAddChildren(child); 
     } 
    } 
} 

を私は両方のを呼び出すと信じていますノードをコンテキストにアタッチすると、すべての子と孫が接続されるため、とAddが重要です(上の単純な例とは対照的に)。 (つまり、彼らは現在Unchanged状態にあり、EFは既存のオブジェクトとしてノードの下にあるサブグラフ全体を考慮します)。したがって、ループがツリー内の次のレベルに達すると、Addedを明示的に(Addを呼び出して)新しい子ノード(ID == 0)があります。 Addを呼び出す場合も同様です。したがって、ノードが存在する場合は、子ノードの状態を再度Unchangedにリセットする必要があります。

編集2:

おそらく、ループの最初のAttachOrAddChildrenを呼び出すことはあり賢く:

 //... 
     foreach(var child in node.Children) 
     { 
      AttachOrAddChildren(child); 
      if (child.ID > 0) 
       context.Nodes.Attach(child); 
      else 
       context.Nodes.Add(child); 
     } 
     //... 

木が根に葉からトラバースされるだろうとEFは変更する必要はないでしょう。この方法既にコンテキスト内にあるオブジェクトの状態それはパフォーマンスに優れているかもしれませんが、わかりません。おそらくそれはどちらの方法でも関係ありません。

+0

これは機能しますが、子供がいるノードがある場合はどうなりますか? 'while(childNode.Children!= null)'や何かする必要がありますか? – OpticalDelusion

+0

@OpticalDelusion:答えにEditを入れました。それが動作するかどうかは分かりませんが、試してみる価値があります。 – Slauma

+0

ええ、私は再帰の代わりにwhileループを使ってこれを行うことを計画していました。入力いただきありがとうございます。 – OpticalDelusion

関連する問題