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

Posted

技术标签:

【中文标题】将类别父 ID 自引用表结构映射到 EF Core 实体【英文标题】:Map category parent id self referencing table structure to EF Core entity 【发布时间】:2017-09-11 16:57:38 【问题描述】:

数据库表:

我尝试使用这种方法将类别表映射到 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;

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

【问题讨论】:

我就是这样做的:***.com/a/63577137/804385 【参考方案1】:

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

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

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

public override IEnumerable<Category> GetAll()
 
    return Table
       .AsEnumerable()
       .Where(x => x.ParentId == null)
       .ToList();

【讨论】:

感谢您的帮助,您的解决方案完美运行。 DV-er 可能认为您应该创建一个触发无数查询的递归方法。但是伊万,如果没有Include,这难道不应该有效吗? 嗨@Gert,好点,实际上是这样!现在我记得我们已经讨论了 EF6 的这种行为。唯一的区别是叶节点得到null 没有Include 的子节点和空集合。当然如果集合成员总是被初始化,那也没什么区别。 天啊。你国王:) 非常感谢伙计,我疯了哈哈,干杯

以上是关于将类别父 ID 自引用表结构映射到 EF Core 实体的主要内容,如果未能解决你的问题,请参考以下文章

EF 4.1 复杂关系的实体映射

使用 EntityFramework.Core 从自引用表加载完整的层次结构

EF Core 5 - 如何将 EF.Functions.Like 与映射到 JSON 字符串的自定义属性一起使用?

Symfony 2 自引用映射的属性“父”构建形式都不是

从自引用表中获取层次结构数据

在 EF 4.1 中映射子类是不是需要 ID 属性?