在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
)由于ParentCategory
和SubCategories
之间的关系。{/ p>
进行此类投影的最佳和优雅方式是什么(不取消父母)? (或者有没有?)
任何帮助都将不胜感激。
谢谢,
答案 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