Gerarchia Problema - > Sostituire la ricorsione con Linq Join?
-
07-07-2019 - |
Domanda
Ho una tabella autoreferenziale, che ha ID, ParentID (nullable).
Quindi, la tabella contiene molti nodi, ogni nodo potrebbe essere la radice nella gerarchia (parent è null) o qualsiasi livello della gerarchia (parent esiste altrove nella tabella).
Dato un nodo iniziale arbitrario, esiste un'elegante query linq che restituirà tutti i figli della gerarchia da quel nodo?
Grazie.
Soluzione
Se vuoi selezionare tutti i figli diretti di un nodo, una semplice query come la seguente dovrebbe fare il lavoro:
from item in table
where item.ID == parentID;
select item
Se vuoi selezionare tutti i discendenti di un nodo, questo non è possibile con LINQ, perché richiede la ricorsione o uno stack che LINQ (e SQL) non fornisce.
Vedi anche:
- StackOverflow: LINQ to SQL per le tabelle autoreferenziali?
- CodeProject: T-SQL - Come ottenere tutti i discendenti di un determinato elemento in un tabella gerarchica
- StackOverflow: Espressione della ricorsione in LINQ
Altri suggerimenti
Eccone uno veloce che ho appena scritto:
class MyTable
{
public int Id { get; set; }
public int? ParentId { get; set; }
public MyTable(int id, int? parentId) { this.Id = id; this.ParentId = parentId; }
}
List<MyTable> allTables = new List<MyTable> {
new MyTable(0, null),
new MyTable(1, 0),
new MyTable(2, 1)
};
Func<int, IEnumerable<MyTable>> f = null;
f = (id) =>
{
IEnumerable<MyTable> table = allTables.Where(t => t.Id == id);
if (allTables
.Where(t => t.ParentId.HasValue && t.ParentId.Value == table
.First().Id).Count() != 0)
return table
.Union(f(
allTables.Where(t => t.ParentId.HasValue && t.ParentId.Value == table
.First().Id).First().Id));
else return table;
};
Ma credo che sia possibile utilizzare SQL con un Union ALL.
So che questo è un vecchio post, ma dovresti dare un'occhiata a questa estensione:
http://www.scip.be/index.php?Page=ArticlesNET23
L'ho usato e funziona alla grande.
Fondamentalmente vado con qualcosa del genere come discusso nel link SO che hai proposto.
public IQueryable GetCategories(Category parent)
{
var cats = (parent.Categories);
foreach (Category c in cats )
{
cats = cats .Concat(GetCategories(c));
}
return a;
}
I CTE sono probabilmente la soluzione migliore, ma per ora vorrei mantenere le cose tutte sullo stesso livello.