我有一个包含一对多自引用关系的数据表:
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。
答案 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?,这是一个类似性质的问题。