LINQ:计算嵌套元素的总数

时间:2010-08-29 18:04:14

标签: c# linq

我有一个包含一对多自引用关系的数据表:

Plant
{ 
  ID,
  Name,
  ParentID
}

我正在尝试创建一个linq查询,它会告诉我源于一个工厂的后代总数。

示例:

我有5种植物:

One {ID = 1, Name = Pine, Parent = null};// This is the root
    Two {ID = 2, Name = Evergreen, Parent = 1}
        Three {ID = 3, Name = Winter Evergreen, Parent = 2}
    Four {ID = 4, Name = Christmas Tree, Parent = 1}
Five {ID = 5, Name = Maple, Parent = null};// This is the root

当我使用ID = 1的输入调用我的LINQ查询时,我希望它为return 3,因为有3个One的后代;二,三和四。 Five不是一个人的继承人。

我能想到的唯一方法是在内部结果上嵌套递归linq查询。我不知道如何做到这一点,我觉得有一种更简单的方法。

如果重要的话,我正在使用C#4.0和LINQ。

2 个答案:

答案 0 :(得分:0)

编辑通过@Kirk Woll评论添加支持LINQ to SQL的代码。

class Program
{
    private static Table<Plant> plants;

    static void Main(string[] args)
    {
        using (var dataContext = new DataClasses1DataContext())
        {
            plants = dataContext.Plants;
            var treesNodes = GetTreesNodes(plants.Where(plant => plant.ID == 1));
            Console.WriteLine(string.Join(",",
                                          treesNodes.Select(plant => plant.ID)));
        }
    }

    public static IEnumerable<Plant> GetTreesNodes(IEnumerable<Plant> roots)
    {
        if(!roots.Any())
        {
            return roots;
        }

        var children = roots.SelectMany(root => 
                                    plants.Where(plant => plant.Parent == root));
        return roots.Union(GetTreesNodes(children));
    }

}

与LINQ to Objects匹配的早期版本:

此方法可以提供树中的所有节点:

public IEnumerable<Plant> GetTreesNodes(IEnumerable<Plant> roots)
{
    if(!roots.Any())
    {
        return Enumerable.Empty<Plant>();
    }

    var rootsIds = roots.Select(root => root.ID);
    var children = plants.Where(plant => rootsIds.Contains(plant.Parent));
    return roots.Union(GetTreesNodes(children));
}

可以在以下示例中使用:

[Test]
public void ExampleTest()
{
    var One = new Plant() {ID = 1, Name = "Pine", Parent = 0}; 
    var Two = new Plant() {ID = 2, Name = "Evergreen", Parent = 1};
    var Three = new Plant() {ID = 3, Name = "Winter Evergreen", Parent = 2};
    var Four = new Plant() {ID = 4, Name = "Christmas Tree", Parent = 1};
    var Five = new Plant() {ID = 5, Name = "Maple", Parent = 0};

    plants = new []{One,Two,Three,Four,Five};

    var nestedElements = GetTreesNodes(new[]{One});
    var numOfNestedElementsWithoutRoot = nestedElements.Count()-1;

    Assert.That(numOfNestedElementsWithoutRoot, Is.EqualTo(3));
}

代码假定没有循环引用。

答案 1 :(得分:0)

如果您使用的是SQL Server,那么您希望构建的查询将是:

DECLARE @TargetElementId int
SET @TargetElementId = 1;

WITH Plants AS
(
    SELECT ID, Name, ParentId
    FROM PlantsTable
    WHERE ParentId = @TargetElementId

    UNION ALL

    SELECT ID, Name, ParentId
    FROM PlantsTable
    INNER JOIN Plants ON PlantsTable.ParentId = Plants.ID
)
SELECT COUNT(ID) - 1 FROM Plants

我不相信LinqToSql可以做到这一点,请参阅Common Table Expression (CTE) in linq-to-sql?,这是一个类似性质的问题。