C# 递归LINQ调用

C# 递归LINQ调用,c#,linq,C#,Linq,我试图用父子关系构建一些数据的XML树,但是在同一个表中 这两个重要领域是: 竞争 家长竞争ID 有些数据可能是 CompetitionID=1, ParentCompetitionID=null 竞争ID=2, ParentCompetitionID=1 竞争ID=3, ParentCompetitionID=1 我使用的中断查询只是以平面格式显示结果。鉴于我正在使用XML,需要某种递归功能。我可以使用普通for循环递归来实现这一点,但我想看看linq版本。谢谢你的帮助 var results

我试图用父子关系构建一些数据的XML树,但是在同一个表中

这两个重要领域是:

竞争 家长竞争ID

有些数据可能是

CompetitionID=1, ParentCompetitionID=null

竞争ID=2, ParentCompetitionID=1

竞争ID=3, ParentCompetitionID=1

我使用的中断查询只是以平面格式显示结果。鉴于我正在使用XML,需要某种递归功能。我可以使用普通for循环递归来实现这一点,但我想看看linq版本。谢谢你的帮助

var results = 
        from c1 in comps
        select new {
            c.CompetitionID,
            SubComps=
                from sc in comps.Where (c2 => c2.CompetitionID == c1.CompetitionID)
                select sc
        };
更新 我发现了Chris Eargle的一篇有趣的文章,它向您展示了如何递归调用lambda委托。这是代码。谢谢你,克里斯

Func<int, int> factoral = x => x <= 1 ? 1 : x + factoral(--x);

Func<int, int> factoral = null;

factoral = x => x <= 1 ? 1 : x + factoral(--x);

Func factoral=x=>x我使用LINQ的
groupby做了一些非常类似的事情

我不使用LINQ的查询语法,因此如果这是错误的,请原谅:

var results = from c in comps
    group c by c.ParentCompetitionID into g
    select new { ParentId = g.Key, ChildId = g };
当然,如果您的类看起来像:

class Competition {
   int Id;
   string Description;
   Competition ParentCompetition;
}
然后,您可以按整个竞争对手进行分组,而不是仅按ID进行分组,这使得生成XML更快、更容易

var results = from c in comps
    group c by c.ParentCompetition into g
    select new { Parent = g.Key, Child = g };
班级竞赛
{ 
int ID{get;set;}
int ParentID{get;set;}
IEnumerable子项{get;set;}
}
公共IEnumerableGetChildren(
IEnumerable竞赛,int parentID)
{
数不清的孩子=
其中(c=>c.ParentID==ParentID);
if(children.Count()==0)
返回null;
返回子项。选择(
c=>新竞争{ID=c.ID,Children=GetChildren(c.ID)};
}
然后您可以调用GetChildren,将根的ID作为parentID传递,这将返回一个树结构。您还可以将
Competition
对象更改为您选择的XML API


我知道这并不完全是您想要的,但是afaik LINQ不支持递归。尽管如此,LINQ的LIN部分意味着语言集成,这正是我所使用的。

而您不能通过单个查询来实现这一点(除非您使用CTE直接调用SQL),您可以将查询数限制为树的深度

代码太长,无法粘贴,但基本步骤如下:

  • 收集根节点并添加到“所有”节点
  • 收集“所有”节点中具有父节点的节点(将列表传递到查询)
  • 将步骤2中的节点添加到“所有”节点
  • 重复2-3,直到步骤2返回0个节点(我认为应该是树的深度+1)

  • 您可以在步骤2中最小化传递给查询的节点数量。SQL server往往会以2000多个条目的列表爆炸。(SQL Compact没有这方面的问题)。

    不知道如何编写递归LINQ。但我认为这里实际上不需要递归。树可以通过两个步骤构建:

    Dictionary<int, Competition> dic = comps.ToDictionary(e => e.CompetitionID);
    foreach (var c in comps)
        if (dic.ContainsKey(c.ParentCompetitionID))
            dic[c.ParentCompetitionID].Children.Add(c);
    var root = dic[1];
    
    Dictionary dic=comps.ToDictionary(e=>e.CompetitionID);
    foreach(公司中的var c)
    if(dic.ContainsKey(c.家长竞争ID))
    dic[c.ParentCompetitionID].Children.Add(c);
    var root=dic[1];
    
    根变量现在包含完整的树

    这里有一个完整的样本要测试:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApplication2
    {
        class Competition
        {
            public int CompetitionID;
            public int ParentCompetitionID;
            public List<Competition> Children=new List<Competition>();
            public Competition(int id, int parent_id) 
            { 
                CompetitionID = id; 
                ParentCompetitionID = parent_id; 
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                List<Competition> comps = new List<Competition>()
                {
                    new Competition(1, 0), 
                    new Competition(2,1),
                    new Competition(3,1),
                    new Competition(4,2),
                    new Competition(5,3)
                };
    
                Dictionary<int, Competition> dic = comps.ToDictionary(e => e.CompetitionID);
                foreach (var c in comps)
                    if (dic.ContainsKey(c.ParentCompetitionID))
                        dic[c.ParentCompetitionID].Children.Add(c);
                var root = dic[1];
            }
        }
    }
    
    使用系统;
    使用System.Collections.Generic;
    使用System.Linq;
    命名空间控制台应用程序2
    {
    班级竞赛
    {
    公共国际竞争d;
    公共竞争ID;
    public List Children=new List();
    公开竞赛(国际id、国际家长id)
    { 
    CompetitionID=id;
    ParentCompetitionID=父项id;
    }
    }
    班级计划
    {
    静态void Main(字符串[]参数)
    {
    列表comps=新列表()
    {
    新竞赛(1,0),
    新竞赛(2,1),
    新比赛(3,1),
    新竞赛(4,2),
    新比赛(5,3)
    };
    字典dic=comps.ToDictionary(e=>e.CompetitionID);
    foreach(公司中的var c)
    if(dic.ContainsKey(c.家长竞争ID))
    dic[c.ParentCompetitionID].Children.Add(c);
    var root=dic[1];
    }
    }
    }
    
    您可以得到一个树状结构,将LINQ和递归与委托相结合。在本例中,我使用如下XML结构:

    <Competitions>
      <Competition ID="1" />
      <Competition ID="2" ParentCompetitionID="1" />
      <Competition ID="3" ParentCompetitionID="1" />
      <Competition ID="4" />
    </Competitions>
    
    class Competition
    {
       public int CompetitionID { get; set; }
    
       public IEnumerable<Competition> Childs { get; set; }
    }
    
    
    
    因此,要以代码形式存储节点数据并方便导航,请创建如下类:

    <Competitions>
      <Competition ID="1" />
      <Competition ID="2" ParentCompetitionID="1" />
      <Competition ID="3" ParentCompetitionID="1" />
      <Competition ID="4" />
    </Competitions>
    
    class Competition
    {
       public int CompetitionID { get; set; }
    
       public IEnumerable<Competition> Childs { get; set; }
    }
    
    班级竞赛
    {
    公共整数竞争ID{get;set;}
    公共IEnumerable子项{get;set;}
    }
    
    现在使用Linq to XML将XML文件加载到XDocument中。然后声明一个委托,该委托迭代文档中的所有XML元素,选择id与委托id参数匹配的节点。选择每个节点时,它再次调用委托,传递要查找的父节点的id。它首先从h将id参数设置为null,因此,选择firts根节点:

        var doc = XDocument.Load("tree.xml");
    
        //Declare the delegate for using it recursively
        Func<int?, IEnumerable<Competition>> selectCompetitions = null;
    
        selectCompetitions = (int? id) =>
        {
           return doc.Elements("Competitions").Elements().Where(c => 
           {
             //If id is null return only root nodes (without ParentCompetitionID attribute)
             if (id == null)
                return c.Attribute("ParentCompetitionID") == null;
             else
                //If id has value, look for nodes with that parent id
                return  c.Attribute("ParentCompetitionID") != null &&
                        c.Attribute("ParentCompetitionID").Value == id.Value.ToString();
            }).Select(x => new Competition() 
                           { 
                          CompetitionID = Convert.ToInt32(x.Attribute("ID").Value),
                          //Always look for childs with this node id, call again to this
                          //delegate with the corresponding ID
                          Childs = selectCompetitions(Convert.ToInt32(x.Attribute("ID").Value))
                           });
    };
    
    var competitions = selectCompetitions(null);
    
    var doc=XDocument.Load(“tree.xml”);
    //声明用于递归使用的委托
    Func=null;
    选择竞赛=(int?id)=>
    {
    返回doc.Elements(“竞赛”).Elements()。其中(c=>
    {
    //如果id为null,则仅返回根节点(不带ParentCompetitionID属性)
    if(id==null)
    返回c.Attribute(“ParentCompetitionID”)==null;
    其他的
    //如果id有值,则查找具有该父id的节点
    返回c.Attribute(“ParentCompetitionID”)!=null&&
    c、 属性(“ParentCompetitionID”).Value==id.Value.ToString();
    }).选择(x=>new Competition()
    { 
    CompetitionID=转换为32(x.Attribute(“ID”).Value),
    //始终查找具有此节点id的child,请再次调用此
    //具有相应ID的委托
    Childs=selectCompetitions(Convert.ToInt3
    
    internal class Competition
    {
        public int CompetitionID;
        public int ParentCompetitionID;
    
        public Competition(int id, int parentId)
        {
            CompetitionID = id;
            ParentCompetitionID = parentId;
        }
    }
    
    internal class Node
    {
        public Node(int id, IEnumerable<Node> children)
        {
            Children = children;
            Id = id;
        }
    
        public IEnumerable<Node> Children { get; private set; }
        public int Id { get; private set; }
    }
    
    internal class Program
    {
        static void Main(string[] args)
        {
            var comps = new List<Competition>
                            {
                                new Competition(1, 0),
                                new Competition(2, 1),
                                new Competition(3, 1),
                                new Competition(4, 2),
                                new Competition(5, 3)
                            };
    
            Node root = ToTree(0, comps);
        }
    
        static readonly Func<int, IEnumerable<Competition>, Node> ToTree = 
            (nodeId, competitions) => new Node(nodeId, from c in competitions where c.ParentCompetitionID == nodeId select ToTree(c.CompetitionID, competitions));
    }