映射类别父ID自引用表结构到EF Core实体

lsc*_*mid 8 c# sql categories self-referencing-table entity-framework-core

数据库表:

图片

我尝试了这种方法将类别表映射到EF核心:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Category>(entity =>
    {
        entity
            .HasMany(e => e.Children)
            .WithOne(e => e.Parent) 
            .HasForeignKey(e => e.ParentId);
    });
}
Run Code Online (Sandbox Code Playgroud)

实体:

[Table("Category"]
public class Category : EntityBase
{
    [DataType(DataType.Text), MaxLength(50)]
    public string Name { get; set; }

    public int? ParentId { get; set; }

    public int? Order { get; set; }

    [ForeignKey("ParentId")]
    public virtual Category Parent { get; set; }

    public virtual ICollection<Category> Children { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后在存储库中:

public override IEnumerable<Category> GetAll()
{ 
    IEnumerable<Category> categories = Table.Where(x => x.Parent == null).Include(x => x.Children).ThenInclude(x=> x.Children);
    return categories;
}
Run Code Online (Sandbox Code Playgroud)

无论您调用Include()还是ThenInclude()多少次,都无法返回3个级别之后的任何内容.

我最后自己编写代码,用递归函数填充子类别:

public override IEnumerable<Category> GetAll()
{
    IEnumerable<Category> categories = Table.Where(x => x.Parent == null).ToList();
    categories = Traverse(categories);
    return categories;
}

private IEnumerable<Category> Traverse(IEnumerable<Category> categories)
{
    foreach(var category in categories)
    {
        var subCategories = Table.Where(x => x.ParentId == category.Id).ToList();
        category.Children = subCategories;
        category.Children = Traverse(category.Children).ToList();
    }
    return categories;
}
Run Code Online (Sandbox Code Playgroud)

有没有人知道更好的方法来编写存储过程来获取表层次结构并映射到我在示例中提供的Category实体?

Iva*_*oev 12

由于缺乏递归表达/ CTE支持,EF(和一般的LINQ)在加载树状数据时遇到问题.

但是如果你想加载整个树(而不是过滤树分支),有一个简单的Include基础解决方案.您只需要一个单独Include的EF导航属性修复就能为您完成工作.当您需要获取样本中的根节点时,诀窍是通过切换到LINQ to Objects上下文(AsEnumerable()通常使用)来实现查询(并修复导航属性)后应用过滤器.

因此,以下应该使用单个SQL查询生成所需的结果:

public override IEnumerable<Category> GetAll()
{ 
    return Table
       .Include(x => x.Children)
       .AsEnumerable()
       .Where(x => x.Parent == null)
       .ToList();
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的帮助,您的解决方案完美无缺。 (2认同)
  • 我的天啊。你国王:) (2认同)