【问题标题】:Reading leaf nodes from a adjacency model list using Linq使用 Linq 从邻接模型列表中读取叶节点
【发布时间】:2014-11-11 13:07:30
【问题描述】:

我有一个邻接模型列表来存储如下层次结构。表结构类似于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()

但不幸的是,我的扩展方法进入了无限循环,我无法弄清楚为什么.. 有人可以帮忙吗..

【问题讨论】:

  • 为什么你的 LevelDeep 中有 item 和 rootItemId?这不应该基本上是一样的吗?

标签: c# linq


【解决方案1】:

我认为您在递归调用中忘记了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 类型,以确保。

【讨论】:

  • 我的 LevelDeep() 逻辑基于这样的想法,即在第一次调用中,我们将拥有房间项目的 Id,但从下一次调用开始,我们将拥有父对象。我仍然不知道为什么我的电话会无限。:(。我想我对使用“产量”有一些误解,但还没有找到任何解决方案......
【解决方案2】:

我终于解决了!

问题:它实际上不是无限循环。它发生在 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 扩展。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多