Proiezione di quadro multi livello autoreferenziali in Entity Framework 6

Proiezione di quadro multi livello autoreferenziali in Entity Framework 6.

Diciamo che ho un’ quadro di Category come segue:

 public class Category { public int CategoryId { get; set; } public int? ParentCategoryId { get; set; } public string Name { get; set; } public string Description { get; set; } public virtual Category ParentCategory { get; set; } public virtual ICollection SubCategories { get; set; } public virtual ICollection Products { get; set; } public Category() { SubCategories = new HashSet(); Products = new HashSet(); } } 

E vorrei mappare l’intera Category DbSet con tutta la gerarchia ad una successiva class POCO (includendo tutti i possibili livelli di categorie secondarie e parentali):

 public class CategoryView { public int Id { get; set; } public int? ParentCategoryId { get; set; } public string Name { get; set; } public string Description { get; set; } public CategoryView ParentCategory { get; set; } public List SubCategories { get; set; } public int ProductCount { get; set; } public Category() { SubCategories = new HashSet(); } } 

Si prega di tenere presente che una singola categoria può avere livelli illimitati di sottocategorie come segue:

 Category (Level 0) SubCategory1 (Level 1) SubCategory2 SubCategory2SubCategory1 (Level 2) SubCategory2SubCategory2 SubCategory2SubCategory2SubCategory1 (Level 3) ... (Level N) SubCategory3 

Quando si tenta di creare una gerarchia ricorsiva con un metodo che tenta di elaborare tutte le categorie secondarie e parentali, ottiene l’ stackoverflow exception , poiché rimane bloccata tra la prima categoria ( Category ) e la prima sottocategoria ( SubCategory1 ) a causa della relazione tra ParentCategory e SubCategories .

Qual è il modo migliore ed elegante di fare tale proiezione (senza eliminare i genitori)? (O c’è qualche?)

Qualsiasi aiuto sarebbe apprezzato.

Grazie,

Non posso dire se è il modo migliore o elegante, ma è un modo abbastanza standard ed efficiente non ricorsivo per build tale struttura.

Inizia con il caricamento di tutte le categorie senza collegamenti di oggetti padre / figlio usando una semplice proiezione:

 var allCategories = db.Categories .Select(c => new CategoryView { Id = c.CategoryId, ParentCategoryId = c.ParentCategoryId, Name = c.Name, Description = c.Description, ProductCount = c.Products.Count() }) .ToList(); 

quindi creare una struttura di dati di ricerca rapida per trovare CategoryView per Id :

 var categoryById = allCategories.ToDictionary(c => c.Id); 

quindi collega le sottocategorie ai loro genitori utilizzando le strutture di dati precedentemente preparate:

 foreach (var category in allCategories.Where(c => c.ParentCategoryId != null)) { category.ParentCategory = categoryById[category.ParentCategoryId.Value]; category.ParentCategory.SubCategories.Add(category); } 

A questo punto, i collegamenti ad albero sono pronti. A seconda delle tue esigenze. o restituire le allCategories o le categorie root se è necessaria una rappresentazione ad albero reale:

 return allCategories.Where(c => c.ParentCategoryId == null); 

PS In realtà l’elenco allCategories può essere evitato, poiché categoryById.Values potrebbe servire allo stesso scopo.

Potrebbe non essere elegante, ma una soluzione adatta è quella di avere nel codice un IDictionary condiviso IDictionary . Quando si intende mappare una Category quadro in un controllo di selezione di CategoryView innanzitutto se è già stato creato questo object e impostato il riferimento archiviato nel dizionario anziché creare un’istanza di CategoryView . Quando si crea una nuova istanza, memorizzarla nel dizionario. Questo è un modo per sfruttare la chiave primaria della tua entity framework per evitare l’infinito problema di ricorsione nel tuo codice.

Inoltre, si noti che nell’object CategoryView non si deve fare riferimento alle istanze di Category . Aggiornalo per fare riferimento a istanze di CategoryView come questa.

 public class CategoryView { public int Id { get; set; } public int? ParentCategoryId { get; set; } // other properties ... public CategoryView ParentCategory { get; set; } public List SubCategories { get; set; } public int ProductCount { get; set; } public CategoryView() { SubCategories = new List(); } }