LINQ父子关系

时间:2011-01-10 09:12:04

标签: asp.net-mvc linq entity-framework .net-3.5

我正在研究MVC中的BLOG功能。我需要能够创建“博客评论”。因此,每条评论可能都有父评论等。

给出表“评论”: CommentId - > int - >身份自我增量 PostId - > INT ParentId - > INT 评论 - >串

alt text

有没有办法获得由CreateDate和ParentId订购的给定文章的评论列表?

或许你可能会建议更好的桌面设计。插入像这样的帖子评论时最好的设计是什么?

我正在使用实体框架。

感谢

4 个答案:

答案 0 :(得分:3)

我想我明白你要做什么。让我看看我是否先拿到它。

给定一个选定的PostID您想要返回所有注释,以便按创建顺序返回顶级注释(即没有父级),并且在每个父级之后和下一次之前按创建顺序返回所有子注释无关的家长评论。是吗?

我创建了以下类和测试数据:

public class Comment
{
    public int CommentId { get; set; }
    public int PostId { get; set; }
    public int? ParentId { get; set; }
    public string Content { get; set; }
}

var comments = new List<Comment>()
{
    new Comment() { PostId = 2, CommentId = 1, },
    new Comment() { PostId = 2, CommentId = 2, ParentId = 1, },
    new Comment() { PostId = 2, CommentId = 3, },
    new Comment() { PostId = 2, CommentId = 4, ParentId = 1, },
    new Comment() { PostId = 2, CommentId = 6, ParentId = 5, },
    new Comment() { PostId = 2, CommentId = 7, ParentId = 1, },
    new Comment() { PostId = 2, CommentId = 8, ParentId = 3, },
    // PostId = 3 to test the filter is working
    new Comment() { PostId = 3, CommentId = 9, },
    // Move this last to test the ordering is working
    new Comment() { PostId = 2, CommentId = 5, ParentId = 3, },
};

我假设CommentId将自动递增,以便可以用它来确定创建顺序。

因此,如果我了解您的要求,您需要以下输出顺序:

id == 1 [pid == ]
id == 2 [pid == 1]
id == 4 [pid == 1]
id == 7 [pid == 1]
id == 3 [pid == ]
id == 5 [pid == 3]
id == 6 [pid == 5]
id == 8 [pid == 3]

需要执行的查询是:

var postId = 2;

var childCommentsLookup =
    (from c in comments
     where c.PostId == postId
     orderby c.CommentId
     select c).ToLookup(x => x.ParentId);

此查询不对注释进行排序,但会强制在数据库中执行单个查询,并返回与postId关联的所有注释。

现在有两种方法可以按正确的顺序获得评论。

首先,递归的lambda表达式:

//Must declare this as null to allow recursive calling of the lambda.
Action<int?, ILookup<int?, Comment>> output = null;

output = (p, l) =>
{
    foreach (var c in l[p])
    {
        Console.WriteLine("id == {0} [pid == {1}]", c.CommentId, c.ParentId);
        output(c.CommentId, l);
    }
};

output(null, childCommentsLookup);

其次,您可以使用迭代器方法简单地以正确的顺序获取带有注释的IEnumerable<Comment>

public static IEnumerable<Comment> OrderCommentsRecursively(
    int? parent, ILookup<int?, Comment> lookup)
{
    foreach (var c0 in lookup[parent])
    {
        yield return c0;
        foreach (var c1 in OrderCommentsRecursively(c0.CommentId, lookup))
        {
            yield return c1;
        }
    }
}

foreach (var c in OrderCommentsRecursively(null, childCommentsLookup))
{
    Console.WriteLine("id == {0} [pid == {1}]", c.CommentId, c.ParentId);
}

现在,如果你要创建一个迭代器函数,我会更进一步,做一个很好的方法来直接返回你的结果:

public static IEnumerable<Comment> GetRecursivelyOrderedCommentsByPostId(
    IEnumerable<Comment> comments, int postId)
{
    return OrderCommentsRecursively(null,
        (from c in comments
         where c.PostId == postId
         select c).ToLookup(x => x.ParentId));
}

foreach (var c in GetRecursivelyOrderedCommentsByPostId(comments, postId))
{
    Console.WriteLine("id == {0} [pid == {1}]", c.CommentId, c.ParentId);
}

这两种/三种方法的结果是:

Lambda Expression:
id == 1 [pid == ]
id == 2 [pid == 1]
id == 4 [pid == 1]
id == 7 [pid == 1]
id == 3 [pid == ]
id == 5 [pid == 3]
id == 6 [pid == 5]
id == 8 [pid == 3]

Iterator Call:
id == 1 [pid == ]
id == 2 [pid == 1]
id == 4 [pid == 1]
id == 7 [pid == 1]
id == 3 [pid == ]
id == 5 [pid == 3]
id == 6 [pid == 5]
id == 8 [pid == 3]

Query & Iterator Call:
id == 1 [pid == ]
id == 2 [pid == 1]
id == 7 [pid == 1]
id == 4 [pid == 1]
id == 3 [pid == ]
id == 5 [pid == 3]
id == 6 [pid == 5]
id == 8 [pid == 3]

我希望这会有所帮助。

答案 1 :(得分:1)

假设您有两个级别的评论(大多数网站没有超过2个级别,并且实际上不需要有更多深度)。

您可以在视图中执行以下操作(我假设视图强烈输入Post):

<% foreach(var comment in Model.Comments.Where (c=>c.ParentId == null)) { %>
   <%: comment.Text %}
   <% if (Model.Comments.Count(c=>c.ParentId == comment.Id) > 0) {%>
      <% foreach (var child in Model.Comment.Where(c=>c.ParentId == comment.Id)) {%>
         <%: child.Text %>
      <% } %>
   <% } %>
<% } %>

如果您愿意,可以继续添加更多级别。

希望这有帮助。

答案 2 :(得分:0)

如果你有这样的自我关系,你基本上有一个n级的树结构。您无法在一个查询中获取所有内容,但必须对每个级别进行查询。

在此过程中,创建一个包含行父项的所有id的logicalPath列,以便注释id 6在该列中具有(3; 1)。诀窍是始终保持正确(最好是触发器)。如果最常见的只有一个或两个级别,我会保持简单,并为每个级别创建一个查询

答案 3 :(得分:0)

你可能在这里过分思考。

所有评论都属于某篇博文。您是否需要对它们进行分页是值得怀疑的,因此立即获取帖子的所有注释并使用注释之间的关系仅用于呈现是非常安全的。也就是说,你会得到post.Comments,然后呈现comments.Where(x =&gt; x.Parent == null).OrderBy(x =&gt; x.CreateDate)递归回复。