16

SQLとEntityFramework(ADO.NET Entity Mapping)の両方でこの再帰を使用するのは初めてです。私はCommentsテーブルがあり、テーブルに列が含まれているコメント管理に取り組んでいますNewsID, CommentID, ParentCommentID, IndentLevel, CreatedTime

以下に示すように、特定のニュースアイテムのコメントのリストを取得しようとしています。ここでは、すべてのコメントが親の下の子と作成された時間に従って配置されています。

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

子と親の関係、次に作成された時間を優先する必要があります。

私がこれまでに学んだことは(インターネットリソースと以前のstackoverflow Q / Aから)

  • 示されているように、これらの再帰クエリは低速です。EntityFrameworkを使用してこれを行うのはさらに遅くなります。しかし、それは達成できます。
  • したがって、SQL Serverでストアドプロシージャを作成し、機能インポートを使用して呼び出すことで実行できます。もう1つは、EntityFrameworkで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でのこのCTE再帰を理解するのを手伝ってくれませんか?

前もって感謝します

4

3 に答える 3

29

AFAIKでは、LINQでもEFでも再帰CTEはサポートされていません。解決策は、CTEをビューとして公開することです。EFコードファーストとマイグレーションを使用した再帰クエリまたは階層クエリに関する記事は、 EFコードファーストマイグレーションを使用してそのようなビューをデプロイする方法を示しています。

再帰的なクライアント側の反復を実行してCTEをエミュレートしようとしても、大規模なデータセットに拡張できず、サーバーとのやり取りがおしゃべりになります。IEnumerableEFコードが返されないことに注意してください。IQueryableこれは、各レベルを具体化し、各エントリの次のレベルを個別のリクエストとして連結することを意味します。LINQベースのソリューションは、エントリ数が限られている浅い階層では適切に機能しますが(多くのプロジェクトでは、このようなデータレイアウトを使用でき、ユーザーの投稿/回答が典型的な例です)、多くの要素を含む深い階層では崩壊します。

于 2012-08-13T07:33:44.307 に答える
5

CTEクエリをStoredProcedureに配置し、Codeから呼び出します。EFは、それを行うためのすべての手段を提供します(SPを呼び出して結果を取得します)。私は自分自身のために同じことをしました、うまくいきます。

Linqを使用してCTEクエリに書き込むことはできません linq-to-sqlの共通テーブル式(CTE)?

Sample ArrangeCommentsはそれ自体を呼び出す再帰的なプロシージャですが、私はあえてそのパフォーマンスに疑問を投げかけています。DBからレコードをプルしてから、メモリ内の操作を適用します。

于 2012-08-13T07:34:26.027 に答える
4

この問題について数時間読んだ後、データベースビューを作成する必要がなく、C#でそれを行うことにしました。

注:これは、パフォーマンスが重要でない操作にのみ使用してください。http://nosalan.blogspot.se/2012/09/hierarchical-data-and-entity-framework-4.htmlからの1000ノードのパフォーマンスの例。

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;
        }
    }   
}
于 2017-09-14T19:43:39.137 に答える