You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:如何让EF Core递归加载对象的全部祖辈与后代属性?

解决EF Core递归加载Category层级(祖辈/孙辈)的问题

嘿,我太懂你这种焦头烂额的感觉了——你没操作错,EF Core默认的Include/ThenInclude确实只能处理固定层级的关联,没法自动递归加载所有祖辈或孙辈的Category,这是它的默认设计逻辑,不是你的问题😎

下面分两种场景给你具体的解决方案:

一、加载所有祖辈(向上遍历Parent链)

如果要把目标Category的所有父级、祖父级一直到根节点都加载出来,有两种常用方法:

方法1:用数据库递归CTE查询(高效推荐)

直接借助数据库的递归公共表表达式(CTE)来一次性查出完整的祖辈链,性能最优,适合层级深的场景:

// 先拿到目标Link对应的CategoryId
var targetLink = FP.Links
    .Include(a => a.Category)
    .FirstOrDefault(a => a.TransId == _transId && /* 你的其他条件 */);

if (targetLink?.Category == null) return;

// 用CTE递归查询所有祖辈
var allAncestors = FP.Categories
    .FromSqlRaw(@"
        WITH RECURSIVE CategoryHierarchy AS (
            -- 起始节点:目标Category
            SELECT Id, ParentId, Name FROM Categories WHERE Id = {0}
            UNION ALL
            -- 递归关联父节点
            SELECT c.Id, c.ParentId, c.Name FROM Categories c
            JOIN CategoryHierarchy ch ON c.Id = ch.ParentId
        )
        SELECT * FROM CategoryHierarchy
    ", targetLink.Category.Id)
    .ToList();

查出来的allAncestors就包含了目标Category本身以及所有往上的祖辈,你可以直接用这个集合,或者手动把祖辈链关联到targetLink.Category的Parent属性上。

方法2:手动递归加载(适合层级较浅的场景)

如果你的Category层级不多(比如最多3-4层),可以在内存里手动循环加载父节点,虽然会产生多次数据库查询,但实现简单:

var link = FP.Links
    .Include(a => a.Category)
    .FirstOrDefault(a => a.TransId == _transId && /* 你的其他条件 */);

if (link?.Category != null)
{
    var currentCat = link.Category;
    // 循环加载父节点直到根节点
    while (currentCat.ParentId != null)
    {
        currentCat.Parent = FP.Categories.Find(currentCat.ParentId);
        currentCat = currentCat.Parent;
    }
}

二、加载所有孙辈(向下遍历Children链)

假设你的Category实体定义了ICollection<Category> Children导航属性,要加载所有子级、孙级节点,可以这么做:

方法1:自定义递归Include扩展方法

EF Core没有内置递归Include的功能,但你可以自己写一个扩展方法来实现:

public static class QueryableExtensions
{
    public static IQueryable<TEntity> IncludeRecursive<TEntity, TProperty>(
        this IQueryable<TEntity> query,
        Expression<Func<TEntity, TProperty>> navigationProperty)
        where TEntity : class
        where TProperty : IEnumerable<TEntity>
    {
        var memberExpr = (MemberExpression)navigationProperty.Body;
        var parameter = Expression.Parameter(typeof(TEntity), "e");
        var propertyExpr = Expression.Property(parameter, memberExpr.Member.Name);
        var lambda = Expression.Lambda<Func<TEntity, IEnumerable<TEntity>>>(propertyExpr, parameter);

        // 递归调用Include和ThenInclude
        return query.Include(navigationProperty)
                    .ThenInclude(lambda)
                    .IncludeRecursive(lambda);
    }
}

使用的时候直接调用这个扩展方法:

var link = FP.Links
    .Include(a => a.Category)
    .IncludeRecursive(c => c.Children)
    .FirstOrDefault(a => a.TransId == _transId && /* 你的其他条件 */);

注意哦:这个方法会一直递归到没有子节点为止,如果层级极深,可能会产生庞大的SQL查询,性能上要谨慎评估。

方法2:数据库CTE递归查询(高效推荐)

同样用CTE从目标节点向下递归查询所有孙辈,性能更可控:

var targetLink = FP.Links
    .Include(a => a.Category)
    .FirstOrDefault(a => a.TransId == _transId && /* 你的其他条件 */);

if (targetLink?.Category == null) return;

var allDescendants = FP.Categories
    .FromSqlRaw(@"
        WITH RECURSIVE CategoryHierarchy AS (
            -- 起始节点:目标Category
            SELECT Id, ParentId, Name FROM Categories WHERE Id = {0}
            UNION ALL
            -- 递归关联子节点
            SELECT c.Id, c.ParentId, c.Name FROM Categories c
            JOIN CategoryHierarchy ch ON c.ParentId = ch.Id
        )
        SELECT * FROM CategoryHierarchy
    ", targetLink.Category.Id)
    .ToList();

总结一下

EF Core的Include/ThenInclude只能处理明确指定的固定层级,没法自动递归,这是正常特性,不是你操作失误。递归加载层级数据的核心思路就是:要么借助数据库的CTE能力一次性查询,要么在代码里手动处理递归逻辑,具体选哪种看你的层级深度和性能需求~

内容的提问来源于stack exchange,提问作者TheTalentedDrAl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:33:04