linq 使用Entity Framework Fluent语法或内联语法编写递归CTE

vktxenjb  于 2022-12-06  发布在  其他
关注(0)|答案(3)|浏览(155)

我对SQL和实体框架(ADO.NET实体Map)中的递归都是新手。我正在处理一个注解管理,其中我有一个Comments表,该表包含NewsID, CommentID, ParentCommentID, IndentLevel, CreatedTime列。
我试图获得一个特定新闻项的评论列表,其中所有评论都是根据父项下的子项和创建时间排列的,如下所示:

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

必须优先考虑父子关系,然后是创建时间。
到目前为止我所了解的是(来自互联网资源和以前的stackoverflow Q/A)

  • 如图所示,这些递归查询很慢。使用实体框架做这件事甚至更慢。但它可以实现。
  • 因此,它可以通过在SQL Server中创建一个存储过程,并使用函数导入调用它来完成。另一件事是在实体框架中使用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
但是我已经试着去理解代码,但是徒劳。有没有人能给我一个更好更详细的解释关于在实体框架中编写递归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递归吗?这很好
先谢了

6uxekuva

6uxekuva1#

AFAIK,截至2022年中期,LINQ和EF仍然不支持递归CTE。解决方案是将CTE公开为视图。关于Recursive or hierarchical queries using EF Code First and Migrations的文章介绍了如何使用EF代码优先迁移部署这样的视图。
尝试通过执行递归客户端迭代来模拟CTE并不能扩展到大型数据集,而且会导致与服务器之间的交流过于繁琐。这意味着它具体化每一层,然后将每个条目 * 的下一层连接为单独的请求 *。基于LINQ的解决方案将合理地用于具有有限条目计数的浅层次结构(并且注意,许多项目 * 可以 * 具有这样的数据布局,用户帖子/回答是典型的示例),但是在具有许多元素的深层次结构下将崩溃。

093gszye

093gszye2#

把CTE查询放到StoredProcedure中,然后从Code中调用它。EF提供了所有的方法(调用SP和检索结果)。我自己也做了同样的事情,效果很好。
无法使用Linq写入CTE查询Common Table Expression (CTE) in linq-to-sql?
示例ArrangeComments是一个递归过程,它调用自己,但我敢质疑它的性能。它从数据库中提取记录,然后在内存中应用操作。

new9mtju

new9mtju3#

在花了几个小时阅读这个问题后,我决定用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;
        }
    }   
}

相关问题