首页 文章

LINQ父子关系

提问于
浏览
2

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

给定表“注释”:CommentId - > int - > identity autoincrement PostId - > int ParentId - > int Comment - > string

alt text

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

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

我正在使用Entity框架 .

谢谢

4 回答

  • 1

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

    给定一个选定的 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]
    

    我希望这有帮助 .

  • 0

    假设您有两个级别的评论(大多数网站没有超过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 %>
          <% } %>
       <% } %>
    <% } %>
    

    如果您愿意,可以继续添加更多这样的级别 .

    希望这可以帮助 .

  • 3

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

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

  • 0

    你可能在这里过分思考 .

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

相关问题