LINQ родительские отношения - PullRequest
2 голосов
/ 10 января 2011

Я работаю над функциональностью BLOG в MVC.Мне нужно иметь возможность создавать «комментарии блога».Таким образом, каждый комментарий может иметь родительский комментарий и т. Д.

Данная таблица «Комментарии»: CommentId -> int -> идентификатор автоинкремента PostId -> int ParentId -> int Comment -> строка

alt text

Есть ли способ получить список комментариев для данной статьи, упорядоченный с помощью CreateDate и ParentId?

Или, может быть, есть лучший дизайн таблицы, который вы можете предложить.Каков наилучший дизайн при вставке таких комментариев?

Я использую Entity Framework.

спасибо

Ответы [ 4 ]

3 голосов
/ 12 января 2011

Мне кажется, я понимаю, что ты пытаешься сделать. Дай мне посмотреть, получу ли я это первым.

С учетом выбранного 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.

Теперь вот два способа получить комментарии в правильном порядке.

Во-первых, рекурсивное лямбда-выражение:

//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 голос
/ 10 января 2011

Предположим, у вас есть комментарии, которые имеют глубину 2 уровня (большинство веб-сайтов не имеют более 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 %>
      <% } %>
   <% } %>
<% } %>

Если хотите, можете добавлять больше уровней, подобных этому.

Надеюсь, это поможет.

0 голосов
/ 10 января 2011

Возможно, вы слишком обдумываете вещи здесь.

Все комментарии принадлежат некоторому сообщению в блоге. Сомнительно, что вам понадобится подкачка страниц для них, поэтому довольно безопасно получать все комментарии к посту сразу и использовать отношения между самими комментариями только для рендеринга. То есть вы бы получили post.Comments, а затем визуализировали комментарии. Где (x => x.Parent == null) .OrderBy (x => x.CreateDate) рекурсивно с их ответами.

0 голосов
/ 10 января 2011

Если у вас есть такое отношение к себе, у вас в основном есть древовидная структура с n-уровнями. Вы не можете получить все в одном запросе, но вы должны сделать запрос для каждого уровня.

Обходным путем является создание столбца logicPath, который содержит все идентификаторы родительских строк, так что идентификатор комментария 6 будет иметь (3; 1) в этом столбце. Хитрость заключается в том, чтобы всегда держать это правильно (желательно триггер). Если самое обычное - иметь только один или два уровня, я бы оставил это простым и создал запрос для каждого уровня

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...