按层次排序对象(ParentID)

时间:2012-02-19 16:41:09

标签: .net linq

我有一组包含以下字段的对象:

MessageID int,
Text string,
ParentMessageID int?

一些示例数据:

1 | "Text 1" | null 
2 | "Reply to Text 1" | 1 
3 | "Reply to Text 1 #2" | 1
4 | "Reply to reply to text 1" | 2

我想通过父消息和子消息对此集合进行排序,因此ID将按如下方式排序:

1
2
4 (child of 2)
3

如何使用LINQ进行操作?

    var q = from i in _dbContext.Messages where ... orderby ...

3 个答案:

答案 0 :(得分:1)

首先,您需要为每一行包含一条直到根目录的整个路径:

1 | "Text 1" | "1"
2 | "Reply to Text 1" | "1_2" 
3 | "Reply to Text 1 #2" | "1_3"
4 | "Reply to reply to text 1" | "1_2_4" 

保存评论时直接保存,或者在代码上动态计算(有点贵)。然后,这个列的排序很简单(正如文字一样)

答案 1 :(得分:0)

首先,制作一棵树,然后递归地从根到叶子。有很多方法可以做到这一点,这里有一个:

class Message {

    public Message(int message_id, string text, int? parent_message_id) {
        Debug.Assert(message_id < int.MaxValue);
        MessageID = message_id;
        ParentMessageID = parent_message_id;
        Text = text;
    }

    public readonly int MessageID;
    public readonly string Text;
    public readonly int? ParentMessageID;

    public static IEnumerable<Message> OrderByHierarchy(IEnumerable<Message> messages) {

        // Key: ParentMessageID (null substituted with int.MaxValue).
        // Value: All messages sharing this parent.
        var dict = messages.GroupBy(m => m.ParentMessageID ?? int.MaxValue).ToDictionary(grouping => grouping.Key);

        // For each root, recursively traverse its children.
        return dict[int.MaxValue].SelectMany(root => RecursiveDescent(dict, root));

    }

    static IEnumerable<Message> RecursiveDescent(Dictionary<int, IGrouping<int, Message>> dict, Message parent) {

        yield return parent;

        IGrouping<int, Message> children;
        if (dict.TryGetValue(parent.MessageID, out children))
            foreach (var child in children)
                foreach (var descendent in RecursiveDescent(dict, child))
                    yield return descendent;

    }

    public override string ToString() {
        return string.Format("{0} | {1} | {2}", MessageID, Text, ParentMessageID == null ? "null" : Convert.ToString(ParentMessageID));
    }

}

class Program {

    static void Main(string[] args) {

        var messages = new[] {
            new Message(1, "Text 1", null),
            new Message(2, "Reply to Text 1", 1),
            new Message(3, "Reply to Text 1 #2", 1),
            new Message(4, "Reply to reply to text 1", 2),
        };

        foreach (var m in Message.OrderByHierarchy(messages))
            Console.WriteLine(m);

    }

}

打印:

1 | Text 1 | null
2 | Reply to Text 1 | 1
4 | Reply to reply to text 1 | 2
3 | Reply to Text 1 #2 | 1

答案 2 :(得分:0)

使用SQL Server中的CTE(公用表表达式),您可以实现所需的目标 - 然后您可以将其“持久化”到例如一个视图,并从您的Linq-to-SQL代码中查询该视图。

WITH Hierarchy AS 
(
    SELECT
       ID, ParentID = CAST(ParentID AS INT), MsgText, 
       NodePath = CAST('/' + CAST(ID AS VARCHAR(5)) AS VARCHAR(MAX))
    FROM    
       dbo.MessageTest 
    WHERE
       ParentID IS NULL

    UNION ALL

    SELECT
       m.ID, m.ParentID, m.MsgText, 
       CAST(h.NodePath + '/' + CAST(m.ID AS VARCHAR(5)) AS VARCHAR(MAX))
    FROM    
       dbo.MessageTest m
    INNER JOIN 
       Hierarchy h ON m.ParentID = h.ID
)
SELECT *
FROM Hierarchy
ORDER BY NodePath

这给了我一个输出:

ID  ParentID  MsgText                 NodePath
1   NULL      Text 1    1         /1
2   1         Reply to Text #1        /1/2
4   2         Reply to text #2        /1/2/4
3   1         Reply #2 to Text #1     /1/3