【问题标题】:How to efficiently build a "Get descendants" algorithm in linq-to-entities?如何在 linq-to-entities 中有效地构建“获取后代”算法?
【发布时间】:2013-05-07 13:16:13
【问题描述】:

在像下面这样的树中,每个项目只知道其父 ID 和订单号,那么查询 Foo 的所有后代的好方法是什么?

  • 1:富
    • 2:孩子
      • 3:孙子 1
      • 4:孙子 2
  • 5:酒吧
  • 6:巴兹

我可以得到这样的孩子

var q = from item in foos
        where item.parentid == "Foo"
        select item;

但是我怎样才能在单个查询中将所有后代都获取到任意深度?如果可能,我想避免使用多个查询进行递归。具体来说,我想获得所有可能级别的后代,不仅是子孙,还包括第 n 级子孙。我想我可以使用这种情况下的订单号和类似的查询

var q = from item in foos
        where item.ordernumber > 1 && item.ordernumber < 5
        select item;

但在这种情况下,我不知道如何获得5,即下一个非后代订单号。此时,1 始终是已知的。


编辑:添加了我希望它选择所有后代的被遗忘的细节,而不仅仅是孩子和孙子。

【问题讨论】:

  • 我不确定我是否理解您的问题。你想要foo 的所有孩子吗?
  • 我要 foo 的所有后代,意思是“子、孙 1 和孙 2”,共 3 项。希望能澄清一些事情。各位用户,如果不清楚,请投票支持@JensKloster 的评论,我将编辑问题。
  • 好的。你能展示你的课吗? Foo 是否有 Child 的集合或只有一个。 FooChildGrandChild1Grandchild2是同一类型吗?
  • 这更像是一个关于算法的理论问题 :) 稍后我可能会做一个例子,但现在:它们都是相同的类型,比如说Item。所有项目都具有三个属性,例如 string id "Foo"string parentid nullint ordernumber 1 或另一个示例:string id "Grandchild 1"string parentid "Child"int ordernumber 3 - 根本没有其他属性。
  • 看起来这个问题有点太理论化了,我实际上正在考虑在这一点上删除它,只是解决递归......

标签: algorithm linq linq-to-entities


【解决方案1】:

无法找到答案,所以将我的解决方案放在这里。我使用了一个通过调用递归函数来构建列表的函数。递归函数接受一个 id,将具有该 ID 的行添加到列表(后代),检查该行的子项,如果有子项,则它启动一个 foreach 并为每个子项调用自身,传入子项的 id :

    public List<WikiPageModel> Descendants;        

    public List<WikiPageModel> GetDescendantsOf(int id)
    {
        Descendants = new List<WikiPageModel>();
        GetDescendantsOf_Recursor(id);
        return Descendants;
    }

    public void GetDescendantsOf_Recursor(int id)
    {
        var page = WikiPages.FirstOrDefault(x => x.PageId == id);
        Descendants.Add(page);
        var children = GetChildrenOf(id);
        if (children.Any())
        {
            foreach (var child in children)
            {
                id = child.PageId;
                GetDescendantsOf_Recursor(id);
            }
        }
    }

    public List<WikiPageModel> GetChildrenOf(int pageId)
    {
        return WikiPages.Where(x => x.ParentPageId == pageId).ToList();
    }

【讨论】:

  • 这也是我最终要做的。如果可能的话,我真的很想避免递归,但这似乎是最简单的选择。
【解决方案2】:
public class Element
    {
        public int ID { set; get; }
        public string Name { get; set; }
        public List<Element> Children { get; set; }
    }

static void Main(string[] args)
        {
            List<Element> elements = new List<Element>();
            Element Foo = new Element() { ID = 1, Name = "Foo" };
            Element Child = new Element() { ID = 2, Name = "Child" };
            Element GrandChild1 = new Element() { ID = 3, Name = "GrandChild 1" };
            Element GrandChild2 = new Element() { ID = 4, Name = "GrandChild 2" };
            Element Bar = new Element() { ID = 5, Name = "Bar" };
            Element Baz = new Element() { ID = 6, Name = "Baz" };
            Foo.Children = new List<Element>();
            Foo.Children.Add(Child);
            Child.Children = new List<Element>();
            Child.Children.Add(GrandChild1);
            Child.Children.Add(GrandChild2);
            elements.Add(Foo);
            elements.Add(Bar);
            elements.Add(Baz);
            var query = elements.Where(e => e.Name == "Foo").SelectMany(c => c.Children);
            var query2 = query.Union(query.SelectMany(g => g.Children));

            foreach (var item in query2)
            {
                Console.WriteLine(item.Name);
            }
        }

【讨论】:

  • 这个方法在第三层之后还能用吗?在我看来,它会简单地返回子孙,但不是无限地返回所有第 n 级后代。
  • 是的,我的要求之一是获得第 N 级项目,这是我问题中的真正问题。抱歉,如果不清楚。我知道我可以为此使用递归方法,但我只想避免使用所有那些一次性的上下文(我基本上每个方法使用 1 个上下文)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-02
相关资源
最近更新 更多