在VB.net中使用Linq获取节点的所有子节点

时间:2018-07-10 21:10:56

标签: vb.net linq hierarchical-data

我具有用于在VB.Net中填充TreeList的分层数据表,如下所示:

ID   ParentID    Name
----------------------
1    NUll         a
2    NUll         b
3    2            c
4    1            d
5    3            e
6    5            f
7    6            g
8    5            h

我的问题:  如何在vb.net中使用Linq获取节点(ID)的所有子节点列表? 请帮助我。

1 个答案:

答案 0 :(得分:0)

不幸的是,我对VB的了解不足,无法给您VB的答案。我将用C#给出答案。您可能会理解这个主意。也许您可以添加VB翻译?

您忘记描述您的Node类。我认为您不仅要孩子,而且要孙子(等等)。

class TreeNode
{
    public int Id {get; set;}
    public string Name {get; set;}

    // every node has zero or more sub-nodes:
    public IEnumerable<TreeNode> Nodes {get; set;}
}

您希望层次结构深入到未知的层次。因此,您不能使用标准LINQ函数。但是您可以轻松扩展LINQ来创建自己的LINQ。参见E xtension Function demystified

public static IEnumerable<TreeNode> AsTreeNodes(this IEnumerable<Person> persons)
{
    // Top TreeNodes: all persons without a Parent:
    return persons.AsTreeNodes((int?)null);
}

使用递归返回所有具有parentId的人的节点序列的实际函数:

public static IEnumerable<TreeNode> AsTreeNodes(this IEnumerable<Person> persons, int? parentId)
{
    // Top Nodes: all persons with parentId
    var personsWithParentId = persons.Where(person.ParentId == parentId);

    foreach (var person in personsWithParentId)
    {
        // every person will become one TreeNode with sub-nodes using recursion
        TreeNode node = new TreeNode()
        {
            Id = person.Id,
            Name = person.Name,

            // get all my Children and Children's children:
            Nodes = persons.ToNodeCollection(person.Id),
        };
        yield return node;
    }
}

请注意,我选择返回IEnumerable而不是List / Array。这样,只有在您真正枚举项目时才可以创建它们。因此,如果您不想使用ID为1的人的节点,则将不会创建其所有孩子。

用法:获取“乔治·华盛顿”所有后代的家庭等级:

IEnumerable<Person> allPersons = ...
IEnumerable<TreeNode> allFamilyHierarchies = allPersons.AsTreeNodes();
IEnumerable<TreeNode> washingtonFamily = allFamilyHierarchies
    .Where(familyHierarchy => familyHierarchy.Name == "George Washington");

注意:到目前为止,尚未创建TreeNode,仅创建了IEnumerable。 一旦执行不返回IEnumerable的操作,枚举就会开始, 例如foreachToListAnyFirstOrDefault

因此,所有非华盛顿族都将被忽略,不会创建顶级节点或子节点。

如果仔细观察,您会发现要查找整个集合中我必须枚举的所有子级,找到具有特定ParentId的所有Persons。如果先将所有人分组为具有相同ParentId的组,然后将这些组放入以ParentId为键的Dictionary中,则可以更有效地完成此操作。这样,查找具有ID X的父代的所有子代将非常快:

var personsWithSameParentId = persons.GroupBy(person => person.ParentId);
var dictionary = personsWithSameParentId.ToDictionary(group => group.Key)

字典中的每个元素都具有等于parentId的键,并且所有具有此parentId的人都将其作为元素。

TreeNode CreateNodeForPerson(Person person)
{
    IEnumerable<Person> children = dictionary[person.Id];
    IEnumerable<TreeNode> childNodes = children
        .Select(child => CreateNodeforPerson(child));

    return new TreeNode()
    {
        Id = person.Id,
        Name = person.Name,
        Nodes = childNodes,
     };
}

您将看到相同的递归。但是一旦有了字典,就不必枚举完整的Person集合,只需访问要为其创建点头的Person的子代/子代子代。