13

数据库表:

在此处输入图像描述

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

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

实体:

[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; }
}

然后在存储库中:

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

这有效,但无论您调用多少次 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;
}

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

4

1 回答 1

24

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

但如果您想加载整个树(而不是过滤的树枝),有一个简单Include的解决方案。您只需要一个Include,然后 EF 导航属性修复将为您完成工作。当您只需要获取示例中的根节点时,诀窍是通过切换到 LINQ to Objects 上下文(AsEnumerable()照常使用),在查询已具体化(并且导航属性已修复)后应用过滤器。

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

public override IEnumerable<Category> GetAll()
{ 
    return Table
       .AsEnumerable()
       .Where(x => x.ParentId == null)
       .ToList();
}
于 2017-09-11T17:25:37.133 回答