Recursive LINQ is more of a party trick than anything you'd actually want to use in production code, but here's one way to do it:
XElement nodes = /* load your xml */;
Func<XElement, int, IEnumerable<int>> query = null;
/* BAD CODE, DO NOT USE! */
query = (x,id) =>
x.Elements("Node")
.Where (node => (int)node.Element("ID") == id) // find our node
.Descendants("TID") // this will be empty in the base case
.Select (tid => (int)tid)
.SelectMany(tid =>
query(x,tid) // recurse
.Concat(new[]{tid}) // keep the current TID if its node had no TIDs
)
.Distinct();
var resultOf7423 = query(nodes, 1);
var resultOf7 = query(nodes, 5);
/* END BAD CODE (I HOPE) */
This is baffling and fragile code, and you almost certainly shouldn't use it. Instead, you could create an extension method off of XElement:
public static IEnumerable<int> SelectRelatedIds(this XElement element, int id)
{
if(element == null) throw new ArgumentNullException("element");
return SelectRelatedIdsIterator(element, id)
.Where(i => i != id)
.Distinct();
}
private static IEnumerable<int> SelectRelatedIdsIterator(XElement element, int id)
{
yield return id;
var tids = element.Elements("Node")
.Where (node => (int)node.Element("ID") == id)
.Descendants("TID");
foreach (int tid in tids)
foreach(var i in SelectRelatedIdsIterator(element, tid))
yield return i;
}
This keeps the recursion contained and pretty easy to understand (at least once you get your head around iterator blocks). It's also LINQ-friendly and potentially composable in a way that the first way is not.