我有一个邻接模型列表来存储层次结构,如下所示。表结构类似于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。
我想知道叶节点员工的名单,即不是" Boss"对任何其他员工。在上面的示例中,它们是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()
但不幸的是我的扩展方法进入infinte循环,我无法弄清楚为什么.. 有人可以帮忙..
答案 0 :(得分:0)
我认为您在递归调用中忘记了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
类型,以确保。
答案 1 :(得分:0)
最后我解决了!。
问题:实际上并不是无限循环。它发生在SelectDeep()调用LevelDeep()然后它使用变量level1Data操纵LevelDeep()的结果。由于变量level1Data并不真正评估表达式,每当我尝试访问level1Data时,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扩展之前评估第一个Linq扩展。