【问题标题】:Linq to filter treeLinq 过滤树
【发布时间】:2017-02-08 07:40:32
【问题描述】:

类结构

public clas Item 
{
   public Item Parent { get; set; }

   public string Code { get; set; }
}

示例树

AAA
 - AAB
    - BBB
CCC
 - CCA

所以我想通过 CODE == BBB 过滤树,结果应该是

AAA
 - AAB
    - BBB

但如果我像这样过滤

IQueryable<Item> itemsQuery = GetQuery();
itemsQuery = itemsQuery.Where(x => x.Code == "BBB")

结果不包含父节点。那么,如果父节点的子节点满足一定条件,如何包含父节点呢?

【问题讨论】:

  • 您需要顶部的所有项目还是只需要一个?
  • 尚不清楚 AAA 是否是 AAB 的父级,反之亦然。
  • 如果你想创建一个结构(取决于你的需要),你可以尝试itemsQuery.Where(x =&gt; x.Code == "BBB").Select(x =&gt; x.Parent) 甚至使用new 关键字。
  • 你使用实体框架吗?如果你使用 EF,你应该 Include Parent 属性。如果不是,Item 是否已初始化?到目前为止你做了什么? GetQuery() 是做什么的?
  • 简短的回答是你不能。 LINQ 在线性 IEnumerable 序列上运行,而不是树结构。您要么需要重复访问数据库以获取父记录,要么需要更改数据库结构以更易于访问的方式表示层次结构。

标签: c# .net linq


【解决方案1】:

简单来说,你无法使用 EF 获得递归树。 EF 返回平面集合。但是,有一些解决方法。

变体 1:

添加到您的 Item public Item Root { get; set; }public ICollection&lt;Item&gt; AllSiblings { get; set; } 属性,它将所有项目指向实际根,第二个是另一种方式(所有嵌套项目)。

查询比make看起来像:

IQueryable<Item> itemsQuery = GetQuery().Include(x => x.AllSiblings);
itemsQuery = itemsQuery.Where(x => x.Code == "BBB" || x.AllSiblings.Any(s => s.Code == "BBB")).ToList();

现在您在应用中拥有了所有项目,然后您可以在 C# 中递归地创建树。

变体 2:

您可以进行多个 SQL 查询来获取找到的项目的每个父项。不建议这样做,因为在找到更多结果时它会变得非常慢。

【讨论】:

    【解决方案2】:

    这里很难应用 LinQ,因为没有从父项到子项的引用。应用过滤器后对父级进行简单枚举怎么样?它将为您提供树中所有匹配项目的列表,然后您可能需要以“树”方式打印它。这是一个 BFS 示例

            IQueryable<Item> itemsQuery = items.AsQueryable();
            itemsQuery = itemsQuery.Where(x => x.Code == "BBB");
    
            var bfsQueue = new Queue<Item>(itemsQuery);
            var matchedItemsSet = new HashSet<Item>();
    
            while (bfsQueue.Count > 0) {
                var item = bfsQueue.Dequeue();
                matchedItemsSet.Add(item);
    
                var parent = item.Parent;
                if (parent != null && !matchedItemsSet.Contains(parent))
                {
                    bfsQueue.Enqueue(parent);
                }
            }
    
            foreach (var item in matchedItemsSet) {
                Console.WriteLine(item.Code);
            }
    

    【讨论】:

      【解决方案3】:

      我更喜欢通用方法。

      public static IEnumerable<T> SelectUntil<T>(this T element, Func<T, T> nextMemberSelector, Func<T, bool> stopCondition)
      {
          while (!stopCondition(element))
          {
              yield return element;
              element = nextMemberSelector(element);
          }
      }
      public static IEnumerable<Item> GetAncestors(this Item e)
      {
          // Or don't Skip(1) if you need the child itself included.
          return e.SelectUntil(T => T.Parent, T => T.Parent == null).Skip(1);
      }
      
      private static void Main(string[] args)
      {
          IEnumerable<Item> itemsQuery = GetQuery();
          IEnumerable<Item> filter = itemsQuery.Where(T => T.Code == "BBB");
      
          foreach (Item item in filter)
          {
              Item[] allParents = item.GetAncestors().ToArray();
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-24
        • 2012-07-08
        • 2015-12-03
        • 1970-01-01
        相关资源
        最近更新 更多