2012-08-13 69 views
9

これは、の再帰で、SQLとEntity Framework(ADO.NETエンティティマッピング)の両方で再帰です。私はCommentsテーブルがあり、テーブルが列NewsID, CommentID, ParentCommentID, IndentLevel, CreatedTimeが含まれているコメントの管理に取り組んでいます。以下に示すように、私は、すべてのコメントは、親の下の子供に応じて配置されたときに作成され、特定のニュース項目のコメントの一覧を取得しようとしていますEntity Frameworkを使用した再帰CTEの作成Fluent構文またはインライン構文

CommentID | time | ParentCommentID 
Guid1  | t1 | null 
Guid4  | t4 | Guid1 
Guid2  | t2 | null 
Guid3  | t3 | Guid2 

優先順位を指定する必要があり子の親の関係に、次に作成された時間に割り当てられます。私は(インターネットリソースと前回のstackoverflowのQ/Aから)これまでに

  • ようにされて身を乗り出してきた何

    は、これらの再帰クエリが遅い示します。 Entity Frameworkを使用してこれを行うことはさらに遅くなります。しかし、それは達成することができます。

  • だから、これはSQL Serverでストアドプロシージャを作成し、機能的なインポートを使用して、それを呼び出すことによって行うことができます。もう一つはEntity FrameworkでLinqを使用しています。
  • SQL Serverのでは、それがこの形式で

を使用しているSQL:

WITH cte_name (column_name [,...n]) 
AS 
( 
CTE_query_definition –- Anchor member is defined. 
UNION ALL 
CTE_query_definition –- Recursive member is defined referencing cte_name. 
) 
-- Statement using the CTE 
SELECT * 
FROM cte_name 
  • しかし、これをしようとする前に、私は、LINQのを試してみたいです。私はアイデアを持っている。このリンクに参照のうえいる。このため

https://stackoverflow.com/a/6225373/892788

しかし、私はコードが、無駄で理解しようとしてきました。誰かがEntity Frameworkで再帰的なCTEを書くことについて、より詳細な説明を教えてくれますか?

private IEnumerable<NewsComment> ArrangeComments(IEnumerable<NewsComment> commentsList, string parentNewsComntID, int level) 
{ 
     Guid parentNewsCommentID; 
     if (parentNewsComntID != null) 
     { 
      parentNewsCommentID = new Guid(parentNewsComntID); 
     } 
     else 
      parentNewsCommentID = Guid.Empty; 

     return commentsList.Where(x => x.ParentCommentID == parentNewsCommentID).SelectMany(x => new[] { x }.Concat(ArrangeComments(commentsList, x.NewsCommentID.ToString(), level + 1)); 

} 

そして、私はメソッド内で、この次のように使用しています:

return ArrangeComments(commentList,null , 0); 

私はそれらを試してみましたが、私がどこにもなっていないのですようです。 SQLの再帰の説明がありますがそこLINQのためのあまり例があり、原因あまり馴染みに私のために曖昧です。誰かが私はLINQでもEFで再帰CTEのためのサポートがない私の知る限り、事前

+1

CTEは、 "共通テーブル式" の略で、LINQとは何の関係もありません。 –

+0

Ok Serg Rogovtsev。私は私の質問を編集します。 –

答えて

23

おかげで素晴らしいですのLINQでこのCTEの再帰を理解するのに役立ちます。解決策は、CTEをビューとして公開することです。 Recursive or hierarchical queries using EF Code First and Migrations上の物品は、EFコード最初の移行を使用してこのようなビューを展開する方法を示しています。

再帰的なクライアント側の反復を行うことによってCTEをエミュレートしようとすると、サーバーとのおしゃべり交流に大規模なデータセットと結果を拡張できません。それは各レベルを実体化することを意味し、その後、別の要求として各エントリための次のレベルを連結して、あなたのEFコードがIEnumerableないIQueryableを返す方法に注意してください。LINQベースのソリューションは、限られたエントリ数を持つ浅い階層のための合理的な仕事(と多くのプロジェクトできは、このようなデータレイアウト、ユーザー投稿/典型的な例である答えを持っていることに注意してください)が、多くの要素を持つ深い階層の下に崩れるだろう。

+2

ありがとうございました。私はこれを私のプロジェクトに選んだ。もう一つの解決策を思いつきました。私は親コメントのAA AB ACと賢明なようなAAコメントの子供のためのAAAB AAACというインデックスを紹介します。これは、コメントごとに26 * 26の子コメントに使用できます。リストを取るときに昇順にしてリストを取るようにします。 –

+1

@Diodeかなりきちんとした解決策 – Askolein

+0

また、その階層パスのフィールドに、区切り文字Name1 \ Name2 \ Name3 \ Name4 \を使用することもできます。 – SamFlushing

2

のStoredProcedureにCTEクエリを入れて、[コードから呼び出します。 EFはそれを行うためのすべての手段を提供します(SPを呼び出して結果を取得する)。私は自分のために同じことをした、うまく動作します。

のLINQでCTEクエリへの書き込みはできません Common Table Expression (CTE) in linq-to-sql?

サンプルArrangeCommentsは、自分自身を呼び出す再帰的な手順ですが、私はそれがパフォーマンスだ疑問をあえて。 DBからレコードを取り出し、操作をメモリに適用します。

+1

私はそれを試して、それは完璧に動作します。しかし、配置されたコメントのビューを作成する方がパフォーマンスを考慮すると便利です。すでにソートされたリストに要素を追加することは、再帰を何度も繰り返すよりもオーバーヘッドが少ないためです。答えをありがとうございました。 –

0

この問題について読んで数時間を過ごした後、私はC#でそれを行うことを決定し、データベースビューを作成する必要がありません。

注:非パフォーマンスクリティカルな操作のための使用これだけ。 1000ノードのパフォーマンスがhttp://nosalan.blogspot.se/2012/09/hierarchical-data-and-entity-framework-4.htmlからの例

Loading 1000 cat. with navigation properties took 15259 ms 
Loading 1000 cat. with stored procedure took 169 ms 

コード:

public class Category 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public int? ParentId { get; set; } 

    public virtual Category Parent { get; set; } 

    public virtual ICollection<Category> Children { get; set; } 

    private IList<Category> allParentsList = new List<Category>(); 

    public IEnumerable<Category> AllParents() 
    { 
     var parent = Parent; 
     while (!(parent is null)) 
     { 
      allParentsList.Add(parent); 
      parent = parent.Parent; 
     } 
     return allParentsList; 
    } 

    public IEnumerable<Category> AllChildren() 
    { 
     yield return this; 
     foreach (var child in Children) 
     foreach (var granChild in child.AllChildren()) 
     { 
      yield return granChild; 
     } 
    } 
} 
関連する問題