在实体框架6中投影自引用多级实体

时间:2017-01-09 10:54:54

标签: c# entity-framework 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>();
    }
}

我想将整个Category DbSet与所有层次结构映射到下一个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)由于ParentCategorySubCategories之间的关系。{/ p>

进行此类投影的最佳和优雅方式是什么(不取消父母)? (或者有没有?)

任何帮助都将不胜感激。

谢谢,

2 个答案:

答案 0 :(得分: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)

它可能不太优雅,但合适的解决方案是在代码中包含共享的module Pugin class ApplicationController < ActionController::Base protect_from_forgery with: :exception def self.get_component(component_name, properties = {}) #properties.each |property| do #render :partial => "layouts/pugin/#{component_name}", locals: {properties: property} render_to_string("layouts/pugin/_#{component_name}", :formats => [:html]) #end end end end 。如果您已经创建了此对象并将参考设置为存储在字典中而不是创建IDictionary<int, CategoryView>实例,则首先将实体Category映射到CategoryView检查。创建新实例时,将其存储在字典中。这是一种利用实体主键来避免代码中无限递归问题的方法。

另请注意,在CategoryView对象中,您不应引用CategoryView个实例。将其更新为引用此类Category个实例。

CategoryView