Se guardi il sito web demo in tempo reale CoreUI , vedrai una barra di navigazione sul lato sinistro, con più livelli comprimibili. Voglio implementare qualcosa di simile, ma in modo dinamico. Ho creato una classe semplice:
public class NavItem
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int navItemId { get; set; }
public int sortOrder { get; set; }
public bool isTitle { get; set; }
public bool isDivider { get; set; }
public string cssClass { get; set; }
public string name { get; set; }
public string url { get; set; }
public string icon { get; set; }
public string variant { get; set; }
public string badgeText { get; set; }
public string badgeVariant { get; set; }
public virtual ICollection<NavItem> children { get; set; }
}
Nota la proprietà dei ICollection<NavItem> children
.
Ad ogni modo, l'ho popolata con un set di dati di esempio (l'esempio CoreUI), e salva correttamente nel database, con un campo chiamato NavItemId1 che memorizza l'ID del genitore di qualsiasi bambino. Tutto bene finora.
Ora vorrei interrogarlo, quindi ho fatto l'ovvio:
var nI = db.navItems.ToList();
Questo, piuttosto brillantemente, produce un elenco contenente tutti gli elementi della nav, con la proprietà children
correttamente popolata con i bambini, dove richiesto.
Tuttavia, includeva anche tutti gli elementi figlio a livello di root ... quindi, invece di 15 elementi di livello root, ne ho 40.
Esiste una query linq che posso eseguire che impedirà al livello di root della lista di riempirsi con i figli (cioè escludendo qualsiasi campo in cui il campo NavItemId1! = Null), ma ancora correttamente caricando il resto della struttura?
per esempio questo è quello che ottengo ora:
<-- I want my list to end here
Potrei aggiungere una proprietà booleana isRoot
, quindi eseguire una query post-lettura per eliminare tutti gli elementi a livello di root che non hanno isRoot
impostato, ad es.
var nI = db.navItems.ToList();
nI = nI.Where(p => p.isRoot).ToList();
ma sembra molto corpulento.
Codice di esempio che mostra il problema. In particolare, guarda gli articoli 6 e 16 (1 ° con 1 livello di bambini, 2 ° con 2 livelli).
https://github.com/adev73/EFCore-LoadParentWithChildren-Example
Il risultato che ottieni è corretto. Richiedete tutti i navItem come un elenco e otterrete un elenco con tutti i dispositivi. La correzione dei nodi figli è un bel "bonus" che EF ha fatto per te.
Inoltre, si applica correttamente il filtro nell'elenco delle istanze anziché direttamente al set di dati. Se lo applicassi sul set di dati, i tuoi nodi figli non sarebbero stati regolati! Quindi, per popolare i nodi figli dovresti usare Include, ma questo ti darebbe solo un livello, il che potrebbe non essere sufficiente .. ovviamente potresti aggiungere più include ma questo non sarebbe scalabile ...
Il tuo modo di fare "corpulento" sembra non essere così corpulento, dopotutto. Lo trovo piuttosto elegante.
Potrei sbagliarmi, ma se hai figli, non hai bisogno di un genitore?
public class NavItem
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int navItemId { get; set; }
public int? parentNavItemId { get; set; }
public int sortOrder { get; set; }
public bool isTitle { get; set; }
public bool isDivider { get; set; }
public string cssClass { get; set; }
public string name { get; set; }
public string url { get; set; }
public string icon { get; set; }
public string variant { get; set; }
public string badgeText { get; set; }
public string badgeVariant { get; set; }
public NavItem parentNavItem { get; set; }
public virtual ICollection<NavItem> children { get; set; }
}
Quindi filtreresti da chi non ha un genitore:
var items = db.NavItems
.ToList() // get every item so that relations load correctly
.Where(x => x.parentNavItemId == null) // then filter by top-level
.ToList();