我正在尝试使用父子关系构建一些数据的XML树,但是在同一个表中。
这两个重要领域是
CompetitionID ParentCompetitionID
某些数据可能
CompetitionID = 1, ParentCompetitionID =空
CompetitionID = 2, ParentCompetitionID = 1
CompetitionID = 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 here找到了一篇有趣的文章,向您展示了如何递归调用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);
^添加了代码格式以显示lamba函数 诀窍是首先为Func委托指定null。
答案 0 :(得分:6)
不知道如何编写递归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];
根变量现在包含完整的树。
这是一个完整的测试样本:
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];
}
}
}
答案 1 :(得分:2)
我知道我在这里有点太晚了。但是你说你已经有了一个使用foreach的版本:)所以如果它实际上应该是递归的并且使用linq这将是一个解决方案:
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));
}
答案 2 :(得分: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; }
}
现在使用Linq to XML将xml文件加载到XDocument中。之后声明一个委托,它遍历文档中的所有xml元素,选择具有id代理节点的id节点的节点。选择每个节点时,它再次调用委托,传递父节点的id以进行查找。它首先将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);
要测试它,您可以执行一个简单的重复方法,将树打印到控制台:
private static void Write(IEnumerable<Competition> competitions, int indent)
{
foreach (var c in competitions)
{
string line = String.Empty;
for (int i = 0; i < indent; i++)
{
line += "\t";
}
line += "CompetitionID = " + c.CompetitionID.ToString();
Console.WriteLine(line);
if (c.Childs != null && c.Childs.Count() > 0)
{
int id = indent + 1;
Write(c.Childs, id);
}
}
}
希望它有所帮助!
答案 3 :(得分:0)
我使用LINQ group by
我不使用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 };
答案 4 :(得分:0)
class Competition
{
int ID { get; set;}
int ParentID { get; set; }
IEnumerable<Competition> Children { get; set; }
}
public IEnumerable<Competition> GetChildren(
IEnumerable<Competition> competitions, int parentID)
{
IEnumerable<Competition> children =
competitions.Where(c => c.ParentID == parentID);
if (children.Count() == 0)
return null;
return children.Select(
c => new Competition { ID = c.ID, Children = GetChildren(c.ID) };
}
然后你可以调用GetChildren,将root的ID作为parentID传递,然后返回树结构。您还可以将Competition
对象更改为您选择的XML API。
我知道这不是你想要的,但是afaik LINQ不支持递归。然而,LINQ的LIN部分意味着语言集成,这正是我使用的。
答案 5 :(得分:0)
虽然您无法使用单个查询执行此操作(除非您使用CTE直接调用SQL),但您可以将查询数限制为树的深度。
代码太长而无法粘贴,但基本步骤是:
您可以最小化在步骤2中传递给查询的节点数量.SQL Server倾向于使用超过2000个条目的列表进行轰炸。 (SQL Compact没有这样的问题)。