Problema de jerarquía - > Reemplace la recursión con Linq Join?
-
07-07-2019 - |
Pregunta
Tengo una tabla autorreferencial, que tiene ID, ParentID (anulable).
Entonces, la tabla contiene muchos nodos, cada nodo podría ser la raíz en la jerarquía (el padre es nulo) o cualquier nivel de la jerarquía (el padre existe en otra parte de la tabla).
Dado un nodo inicial arbitrario, ¿hay una consulta linq elegante que devolverá a todos los hijos de la jerarquía de ese nodo?
Gracias.
Solución
Si desea seleccionar todos los hijos directos de un nodo, una consulta simple como la siguiente debería hacer el trabajo:
from item in table
where item.ID == parentID;
select item
Si desea seleccionar todos los descendientes de un nodo, esto no es posible con LINQ, porque requiere recursividad o una pila que LINQ (y SQL) no proporciona.
Ver también:
- StackOverflow: ¿LINQ to SQL para tablas autorreferenciadas?
- CodeProject: T-SQL - Cómo obtener todos los descendientes de un elemento dado en un tabla jerárquica
- StackOverflow: Expresando recurrencia en LINQ
Otros consejos
Aquí hay uno rápido que acabo de escribir:
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;
};
Pero creo que es posible hacerlo usando SQL con Union ALL.
Sé que esta es una publicación anterior, pero debería consultar esta extensión:
http://www.scip.be/index.php?Page=ArticlesNET23
Lo he estado usando y está funcionando muy bien.
Básicamente voy con algo como esto como se discutió en el enlace SO que proporcionó.
public IQueryable GetCategories(Category parent)
{
var cats = (parent.Categories);
foreach (Category c in cats )
{
cats = cats .Concat(GetCategories(c));
}
return a;
}
Los CTE son probablemente la mejor solución, pero por ahora me gustaría mantener todo en el mismo nivel.