2017-01-09 1 views
4

Проектирование самостоятельной ссылки мульти объектов уровня в Entity Framework 6.Проектирование самостоятельной ссылки мульти сущностей на уровне В Entity Framework 6

Давайте предположим, что у меня есть Category сущность следующим образом:

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<Category> SubCategories { get; set; } 
    public virtual ICollection<Product> Products { get; set; } 

    public Category() 
    {    
     SubCategories = new HashSet<Category>(); 
     Products = new HashSet<Product>(); 
    } 
} 

И я хотел бы отобразить весь CategoryDbSet со всей иерархией к следующему классу POCO (а в том числе всех возможных уровней подразделов и родительских категории):

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<CategoryView> SubCategories { get; set; } 

    public int ProductCount { get; set; } 

    public Category() 
    {    
     SubCategories = new HashSet<CategoryView>();    
    } 
} 

Пожалуйста, имейте в виду, что одна категория может иметь неограниченное число уровней подкатегорий следующим образом:

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

Когда пытались создать иерархию с рекурсивного метода, который пытается обработать каждую категорию к югу и родительские категории, получили stackoverflow exception, поскольку он застревает между первой категорией (Category) и первой подкатегорией (SubCategory1) из-за отношения между ParentCategory и SubCategories.

Каков наилучший и элегантный способ сделать такую ​​проекцию (без исключения родителей)? (Или есть?)

Любая помощь будет оценена по достоинству.

Спасибо,

+0

Проецируя, вы хотите создать 'IQueryable ' или 'IEnumerable ' будет работать? Также не следует, чтобы 'ParentCategory' и' SubCategories' 'CategoryView' ссылались на объекты' CategoryView' (а не 'Category', как в сообщении)? –

+0

IvanStoev, jorgonor да, вы оба правы, скопируйте/вставьте ошибку. Класс POCO должен/должен содержать CategoryView вместо категории. ИванСтоев, хотелось бы иметь IEnumerable , который должен иметь в конце результата проекции. Благодарю. на стороне примечания jorgonor, подход словаря также вызывает ошибку stackoverflow при попытке обработать родительские подкатегории. –

+0

Как я уже сказал, это должен быть тот же словарь для каждого вызова функции. Я предполагаю, что вы вызываете рекурсивную функцию для создания последовательности, каждый вызов функции должен совместно использовать этот экземпляр. Это предотвращает создание новых экземпляров с одинаковым идентификатором. Если у вас нет очень большого набора данных, вы не должны видеть исключение StackOverflowException. – jorgonor

ответ

3

Я не могу сказать, что это лучший или элегантным способ, но это довольно стандартные и эффективные не рекурсивный способ построения такой структуры.

Начать с погрузкой все категории без родителей/дочерний объект ссылки используя простую проекцию:

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(); 

затем создать быструю структуру данных подстановок для поиска CategoryView по Id:

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

затем связать подкатегории с родителями, используя ранее подготовленные структуры данных:

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

На данный момент ссылки на дерево готовы. В зависимости от ваших потребностей.либо вернуть allCategories или корневые категории, если вам нужно реальное представление дерева:

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

P.S. Фактически список allCategories можно избежать, так как categoryById.Values может служить той же цели.

+1

Спасибо @ Ивана, это как очарование! –

2

Это не может быть элегантным, но подходящим решением является иметь в своем коде общий IDictionary<int, CategoryView>. Когда вы собираетесь сопоставить объект Category в поле CategoryView, сначала проверьте это, если вы уже создали этот объект и задали ссылку, хранящуюся в словаре, вместо создания экземпляра CategoryView. При создании нового экземпляра сохраните его в словаре. Это способ использовать первичный ключ вашего объекта, чтобы избежать проблемы с бесконечной рекурсией в вашем коде.

Также обратите внимание, что в вашем объекте CategoryView вы не должны ссылаться на экземпляры Category. Обновите его до ссылки CategoryView таких случаев.

public class CategoryView 
{ 

    public int Id { get; set; } 

    public int? ParentCategoryId { get; set; }   

    // other properties ... 

    public CategoryView ParentCategory { get; set; } 

    public List<CategoryView> SubCategories { get; set; } 

    public int ProductCount { get; set; } 

    public CategoryView() 
    {    
     SubCategories = new List<CategoryView>(); 
    } 
}