Linq-to-Sql: рекурсивное получение дочерних элементов

У меня есть таблица комментариев, в которой есть 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 в этой ситуации?

person Shawn    schedule 11.02.2009    source источник
comment
Это Linq to SQL или Linq to Entities?   -  person bendewey    schedule 11.02.2009


Ответы (1)


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

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


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

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

Улучшения:

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

so:

using(var db = new MyDataContext() ) { /* existing code */ }
  • LoadWith стоит попробовать, но я не уверен, что буду надеяться ...
  • список искомых идентификаторов рискован как поле - я думаю, вы в порядке, если не вызываете его дважды ... лично я бы использовал аргумент в частном методе поддержки ... (т.е. список между рекурсивными вызовами, но не в общедоступном API)
person Marc Gravell    schedule 11.02.2009
comment
да, это имеет смысл, я просто не уверен, почему они не позволят вам вернуть ienumerable типа или типа tiself - person Shawn; 11.02.2009
comment
Вы можете вернуть IEnumerable ‹T› типа. Вы можете давать доход Т. - person Marc Gravell; 11.02.2009
comment
правильно, я полностью понимаю, но я думаю, что вы должны уметь делать то и другое - person Shawn; 11.02.2009
comment
Если бы существовали только общие (типизированные) версии, я ожидаю, что это могло бы быть ... проблема в том, что это было бы очень неоднозначно для неуниверсальных версий ... вы можете yield любой объект, чтобы он не знал представляет ли это 1 элемент или последовательность. Лично я доволен тем, что сейчас все просто. - person Marc Gravell; 11.02.2009