Linq-to-Sql: рекурсивно заводить детей - PullRequest
5 голосов
/ 11 февраля 2009

У меня есть таблица комментариев, у которой есть CommentID и ParentCommentID. Я пытаюсь получить список всех детей комментария. Это то, что я имею до сих пор, я еще не проверял это.

private List<int> searchedCommentIDs = new List<int>();
// searchedCommentIDs is a list of already yielded comments stored
// so that malformed data does not result in an infinite loop.
public IEnumerable<Comment> GetReplies(int commentID) {
    var db = new DataClassesDataContext();
    var replies = db.Comments
        .Where(c => c.ParentCommentID == commentID 
            && !searchedCommentIDs.Contains(commentID));
    foreach (Comment reply in replies) {
        searchedCommentIDs.Add(CommentID);
        yield return reply;
        // yield return GetReplies(reply.CommentID)); // type mis-match.
        foreach (Comment replyReply in GetReplies(reply.CommentID)) {
            yield return replyReply;
        }
    }
}

2 вопроса:

  1. Есть ли очевидный способ улучшить это? (Кроме того, возможно, создание представления в SQL с CTE.)
  2. Почему я не могу дать IEnumerable <Comment> для IEnumerable <Comment>, только Comment сам по себе?
  3. Есть ли способ использовать SelectMany в этой ситуации?

1 Ответ

4 голосов
/ 11 февраля 2009

Возможно, я бы использовал UDF / CTE или (для очень глубоких структур) хранимую процедуру, которая делает то же самое вручную.

Обратите внимание, что если вы можете изменить схему, вы можете предварительно индексировать такие рекурсивные структуры в индексированное / ранжированное дерево, которое позволяет выполнить один запрос BETWEEN, но обслуживание дерева обходится дорого (то есть запрос становится дешевым, но вставка / обновление / удаление становятся дорогими, или вам нужно отложенное запланированное задание).


Re 2 - вы можете только yield тип, указанный в перечислении (T in IEnumerable<T> / IEnumerator<T>).

Вы можете yield и IEnumerable<Comment> , если метод вернул IEnumerable<IEnumerable<Comment>> - имеет ли это смысл?

Улучшения:

  • возможно udf (для сохранения компоновки, а не хранимой процедуры), который использует подход рекурсии CTE
  • использовать using, поскольку DataContext равно IDisposable ...

так:

using(var db = new MyDataContext() ) { /* existing code */ }
  • LoadWith стоит попробовать, но я не уверен, что буду полон надежд ...
  • список найденных идентификаторов является рискованным как поле - я думаю, что вы в порядке, если вы не вызываете его дважды ... лично, я бы использовал аргумент для частного метода поддержки ... ( т.е. передать список между рекурсивными вызовами, но не в общедоступном API)
...