Linq-to-Sql: obtenez des enfants récursivement

J’ai une table de commentaire qui a un commentID et un parentCommentID. J’essaie d’obtenir une liste de tous les enfants du commentaire. C’est ce que j’ai jusqu’à présent, je ne l’ai pas encore testé.

private List searchedCommentIDs = new List(); // searchedCommentIDs is a list of already yielded comments stored // so that malformed data does not result in an infinite loop. public IEnumerable 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 questions:

  1. Y a-t-il un moyen évident d’améliorer cela? (Outre peut-être créer une vue dans sql avec un CTE.)
  2. Comment se fait-il que je ne puisse pas céder un IEnumerable IEnumerable à un IEnumerable, uniquement le Comment lui-même?
  3. Est-il possible d’utiliser SelectMany dans cette situation?

J’utiliserais probablement soit un UDF / CTE, soit (pour les structures très profondes) une procédure stockée qui fait la même chose manuellement.

Notez que si vous pouvez modifier le schéma, vous pouvez préindexer de telles structures récursives dans un arbre indexé / à distance qui vous permet de faire une seule requête BETWEEN – mais la maintenance de l’arbre est coûteuse / delete devient coûteux, ou vous avez besoin d’une tâche planifiée retardée).


Re 2 – vous ne pouvez yield le type spécifié dans l’énumération (le T dans IEnumerable / IEnumerator ).

Vous pouvez yield un IEnumerable si la méthode renvoie IEnumerable> – cela a-t-il un sens?

Améliorations:

  • peut-être un udf (pour conserver la composabilité, plutôt qu’une procédure stockée) qui utilise l’approche de récursion CTE
  • utiliser en using , puisque DataContext est IDisposable

alors:

 using(var db = new MyDataContext() ) { /* existing code */ } 
  • LoadWith vaut la peine d’essayer, mais je ne suis pas sûr que j’aurais bon espoir …
  • la liste des identifiants recherchés est risquée en tant que champ – je suppose que tout va bien tant que vous ne l’appelez pas deux fois … personnellement, j’utiliserais un argument sur une méthode de sauvegarde privée … liste entre les appels récursifs, mais pas sur l’API publique)