使用Linq从邻接模型列表中读取叶节点
本文关键字:列表 读取 叶节点 模型 Linq 使用 | 更新日期: 2023-09-27 18:29:53
我有一个邻接模型列表,用于存储如下层次结构。该表结构类似于Nothwind数据库中的employees表。下面给出的示例。
-
员工Id 1向员工Id 2 报告
-
员工Id 3向员工Id 2 报告
-
员工Id 4向员工Id 2 报告
-
员工Id 5向员工Id 3 报告
-
员工Id 6向员工Id 4 报告
-
员工Id 7向员工Id 5 报告
-
员工Id 8向员工Id 7报告。
我想知道叶节点员工的列表,即对任何其他员工来说都不是"老板"的员工。在上述示例中,它们分别为1、8和6。我尝试编写一个LINQ扩展,以获得下面给出的所有叶节点。
public static IEnumerable<TEntity> SelectDeep<TEntity, TProperty>(
this IEnumerable<TEntity> allItems,
Func<TEntity, TProperty> idProperty,
Func<TEntity, TProperty> parentIdProperty,
object rootItemId)
{
IEnumerable<TEntity> leve11Data = LevelDeep(allItems, default(TEntity), idProperty, parentIdProperty, rootItemId);
IEnumerable<TProperty> leafOnly = leve11Data.Select(i => idProperty(i)).Except(leve11Data.Select(i => parentIdProperty(i)));
IEnumerable<TEntity> childItemsOnly = allItems.Where(i => leafOnly.Contains(idProperty(i)));
return childItemsOnly;
}
public static IEnumerable<TEntity> LevelDeep<TEntity, TProperty>(this IEnumerable<TEntity>allItems,
TEntity parentItem,
Func<TEntity, TProperty> idProperty,
Func<TEntity, TProperty> parentIdProperty,
object rootItemId)
{
IEnumerable<TEntity> childs;
if (rootItemId != null)
{
childs = allItems.Where(i => parentIdProperty(i).Equals(rootItemId));
}
else
{
if (parentItem == null)
{
childs = allItems.Where(i => parentIdProperty(i).Equals(default(TProperty)));
}
else
{
childs = allItems.Where(i => parentIdProperty(i).Equals(idProperty(parentItem)));
}
}
if (childs.Count() > 0)
{
foreach (TEntity item in childs)
{
yield return item;
foreach (TEntity subItem in LevelDeep(allItems, item, idProperty, parentIdProperty, null))
{
yield return subItem;
}
}
}
}
我称之为
(from listEntry in myList.SelectDeep(e => e.child_part_id, e => e.parent_part_id, 100).ToList()
但不幸的是,我的扩展方法进入了infint循环,我不知道为什么。。有人能帮忙吗。。
我认为您在递归调用中忘记了rootItemId
参数。尝试将其更改为类似的内容
foreach (TEntity item in childs)
{
yield return item;
object itemId = idProperty(item);
foreach (TEntity subItem in LevelDeep(allItems, item, idProperty, parentIdProperty, itemId))
{
yield return subItem;
}
}
实际上,您应该将所有ID的object
类型更改为TProperty
类型,只是为了确保。
我终于解决了!。
问题:实际上并不是无限循环。SelectDeep()调用LevelDeep(。由于变量level1Data并没有真正计算表达式,所以每当我试图访问level1DData时,LevelDeep()都会被一次又一次地执行。从日志中我误解为无限递归。
解决方案:编写另一个扩展方法FindLeafOnly()作为
public static IEnumerable<TEntity> FindLeafOnly<TEntity, TProperty>(
this IEnumerable<TEntity> leve11Data,
Func<TEntity, TProperty> idProperty,
Func<TEntity, TProperty> parentIdProperty)
{
IEnumerable<TProperty> allChild = leve11Data.Select(i => idProperty(i));
IEnumerable<TProperty> allParent = leve11Data.Select(i => parentIdProperty(i));
IEnumerable<TProperty> leafOnly = allChild.Except(allParent);
IEnumerable<TEntity> childItemsOnly = leve11Data.Where(i => leafOnly.Contains(idProperty(i)));
return childItemsOnly;
}
最后,避免重复表达式求值的关键是在调用FindLeafOnly()之前,在SelectDeep()之后调用ToList()。所以呼叫代码就像一样
(from listItem in myAdjucencyModelList.SelectDeep(e => e.child_part_id, e => e.parent_part_id, 5412).ToList().FindLeafOnly(e=> e.child_part_id, e => e.parent_part_id).ToList()
ToList()确保第一个Linq扩展在调用下一个之前进行了评估。