C# 在实体框架6中投影自参考多级实体

C# 在实体框架6中投影自参考多级实体,c#,entity-framework,entity-framework-6,C#,Entity Framework,Entity Framework 6,在实体框架6中投影自参考多级实体 假设我有一个类别实体,如下所示: public class Category { public int CategoryId { get; set; } public int? ParentCategoryId { get; set; } public string Name { get; set; } public string Description { get; set; } pub

在实体框架6中投影自参考多级实体

假设我有一个
类别
实体,如下所示:

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 (Level 0)
    SubCategory1 (Level 1)
    SubCategory2
        SubCategory2SubCategory1 (Level 2)
        SubCategory2SubCategory2
            SubCategory2SubCategory2SubCategory1 (Level 3)
            ... (Level N)
    SubCategory3
请记住,单个类别可能有无限级别的子类别,如下所示:

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 (Level 0)
    SubCategory1 (Level 1)
    SubCategory2
        SubCategory2SubCategory1 (Level 2)
        SubCategory2SubCategory2
            SubCategory2SubCategory2SubCategory1 (Level 3)
            ... (Level N)
    SubCategory3
当尝试使用递归方法创建层次结构时,该方法尝试处理每个单独的子类别和父类别,得到
堆栈溢出异常
,因为它卡在第一个类别(
类别
)和第一个子类别(
子类别1
)之间由于
父类别
子类别
之间的关系

在不排除父母的情况下,进行这种投射的最佳和优雅的方式是什么?(或者有没有?)

任何帮助都将不胜感激


谢谢,

这可能并不优雅,但一个合适的解决方案是在您的代码中包含一个共享的
IDictionary
。当您要将实体
类别
映射到
类别视图
时,首先检查是否已创建此对象,并设置字典中存储的引用,而不是创建
类别视图
实例。创建新实例时,请将其存储在字典中。这是一种利用实体主键避免代码中无限递归问题的方法

另外,请注意,在
类别视图中
对象不应引用
类别
实例。将其更新为以下引用
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>();
    }
}
公共类类别视图
{
公共int Id{get;set;}
public int?ParentCategoryId{get;set;}
//其他属性。。。
公共类别查看父类别{get;set;}
公共列表子类别{get;set;}
public int ProductCount{get;set;}
公共类别视图()
{            
子类别=新列表();
}
}

我不能说这是最好的还是优雅的方法,但它是构建这种结构的非常标准和高效的非递归方法

首先,使用简单的投影加载没有父/子对象链接的所有类别:

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();
然后创建一个快速查找数据结构,通过
Id
查找
CategoryView

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

另外,实际上可以避免使用
allCategories
列表,因为
categoryById.Values
可以达到相同的目的。

通过投影,您的意思是生成
IQueryable
还是
IEnumerable
?也不应该引用
CategoryView
ParentCategory
SubCategories
,引用
CategoryView
对象(而不是像帖子中那样
Category
)?伊凡斯托耶夫,乔戈诺是的,复制/粘贴错误POCO类是/应该持有CategoryView而不是Category。IvanStoev,希望在预测结果的末尾有一个我们需要的IEnumerable。谢谢另一方面,jorgonor在尝试处理子类别和父类别时,字典方法也会导致stackoverflow错误。正如我所说的,每个函数调用都必须使用相同的字典。我猜您是在调用递归函数来创建序列,每个函数调用都必须共享这个实例。这会阻止创建具有相同ID的新实例。除非您有一个非常大的数据集,否则您不应该看到任何StackOverflowException。谢谢@Ivan,这很有魅力!