我是SQL和实体框架(ADO.NET实体映射)中的递归的新手。我正在进行评论管理,我有一个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:
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
为此,我参考了这个链接,我有了这个想法: 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递归很棒吗
提前致谢
答案 0 :(得分:25)
AFAIK在LINQ和EF中都不支持递归CTE。解决方案是将CTE作为视图公开。 Recursive or hierarchical queries using EF Code First and Migrations上的文章展示了如何使用EF代码首次迁移来部署此类视图。
尝试通过递归客户端迭代来模拟CTE不会扩展到大型数据集并导致与服务器的繁琐交换。请注意您的EF代码如何返回IEnumerable
而非IQueryable
,这意味着它实现了每个级别,然后将每个条目的下一级连接为单独的请求。基于LINQ的解决方案将适用于入口数量有限的浅层次结构(并注意许多项目可以具有此类数据布局,用户帖子/答案是一个典型示例),但会在深层次结构下崩溃很多元素。
答案 1 :(得分:2)
将CTE查询放入StoredProcedure,然后从Code中调用它。 EF提供了执行此操作的所有方法(调用SP并检索结果)。我为自己做了同样的事,工作得很好。
使用Linq写入CTE查询是不可能的 Common Table Expression (CTE) in linq-to-sql?
Sample ArrangeComments是一个自称调用的递归过程,但我不敢质疑它的性能。它从DB中提取记录,然后在内存中应用操作。
答案 2 :(得分:1)
在花了几个小时阅读有关此问题后,我决定在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;
}
}
}