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

如何用LinQ筛选含IsInActivity=true子项的父子Category列表?

解决树形分类的递归筛选问题

这个树形分类的筛选需求我之前也碰到过,核心就是要构建一个保留所有包含有效资源(IsInActivity=true)或后代包含有效资源的分类的树形结构,同时每个分类下只保留符合条件的子分类,并且只展示有效资源。我分两种场景给你解决方案:一种是基于EF(数据库层面先筛选),另一种是内存中递归处理。

方案一:EF中先筛选所有有效分类ID,再构建树

这种方式更高效,因为先在数据库层面找出所有需要保留的分类,再构建树形结构。

步骤1:获取所有需要保留的分类ID

我们需要两类分类ID:

  • 直接关联IsInActivity=true的ExternalResource的分类;
  • 上述分类的所有祖先分类(因为子分类有有效资源时,父分类要展示)。

用递归的方式获取这些ID:

// 1. 先获取所有直接关联有效资源的分类ID
var validCategoryIds = db.ExternalResources
    .Where(er => er.IsInActivity)
    .Select(er => er.CategoryID)
    .Distinct()
    .ToList();

// 2. 递归获取这些分类的所有祖先ID
var allValidIds = new HashSet<long>(validCategoryIds);
var parentIds = validCategoryIds;

do
{
    parentIds = db.Categories
        .Where(c => parentIds.Contains(c.CategoryID))
        .Select(c => c.ParentCategoryID)
        .Where(id => id.HasValue)
        .Select(id => id.Value)
        .Distinct()
        .Except(allValidIds)
        .ToList();
    
    foreach (var id in parentIds)
    {
        allValidIds.Add(id);
    }
} while (parentIds.Any());

步骤2:构建符合要求的树形结构

接下来我们从数据库获取这些有效分类,然后递归构建树,同时过滤子分类和资源:

// 获取所有有效分类并预加载导航属性
var validCategories = db.Categories
    .Where(c => allValidIds.Contains(c.CategoryID))
    .Include(c => c.Subcategories)
    .Include(c => c.ExternalResources)
    .ToList()
    .Select(c => MapToValidCategory(c, allValidIds))
    .ToList();

// 最终结果取根分类(ParentCategoryID为null的)
var finalResult = validCategories.Where(c => !c.ParentCategoryID.HasValue).ToList();

// 辅助方法:映射分类并过滤子分类和资源
private Category MapToValidCategory(Category original, HashSet<long> validIds)
{
    return new Category
    {
        CategoryID = original.CategoryID,
        Name = original.Name,
        ParentCategoryID = original.ParentCategoryID,
        // 只保留属于有效ID的子分类
        Subcategories = original.Subcategories
            .Where(sc => validIds.Contains(sc.CategoryID))
            .Select(sc => MapToValidCategory(sc, validIds))
            .ToList(),
        // 只保留IsInActivity=true的资源
        ExternalResources = original.ExternalResources.Where(er => er.IsInActivity).ToList()
    };
}

方案二:内存中递归筛选整个分类树

如果你的分类数据量不大,可以先加载所有分类到内存,再递归筛选符合条件的节点:

// 加载所有分类并预加载导航属性
var allCategories = db.Categories
    .Include(c => c.Subcategories)
    .Include(c => c.ExternalResources)
    .ToList();

// 递归判断:分类自身或后代是否有有效资源
Func<Category, bool> IsCategoryValid = null;
IsCategoryValid = (cat) => 
    cat.ExternalResources.Any(er => er.IsInActivity) 
    || cat.Subcategories.Any(sc => IsCategoryValid(sc));

// 构建符合条件的树形结构
var finalResult = allCategories
    .Where(c => !c.ParentCategoryID.HasValue)
    .Select(c => BuildValidCategoryTree(c, IsCategoryValid))
    .Where(c => c != null)
    .ToList();

// 辅助方法:递归构建有效分类树
private Category BuildValidCategoryTree(Category original, Func<Category, bool> isValid)
{
    // 如果当前分类不符合条件,直接返回null(不保留)
    if (!isValid(original))
        return null;
    
    return new Category
    {
        CategoryID = original.CategoryID,
        Name = original.Name,
        ParentCategoryID = original.ParentCategoryID,
        // 递归处理子分类,只保留符合条件的
        Subcategories = original.Subcategories
            .Select(sc => BuildValidCategoryTree(sc, isValid))
            .Where(sc => sc != null)
            .ToList(),
        // 过滤有效资源
        ExternalResources = original.ExternalResources.Where(er => er.IsInActivity).ToList()
    };
}

关键说明

两种方案都能满足你的需求:

  • 所有包含有效资源的分类,以及它们的所有父分类都会被展示;
  • 每个分类下的子分类只保留自身或后代有有效资源的节点;
  • 每个分类的ExternalResources仅保留IsInActivity=true的项。

如果用EF Core,还可以用递归CTE来更高效地获取所有有效分类ID,替代方案一中的递归循环:

// 递归CTE获取所有有效分类ID(自身有有效资源或后代有)
var cte = db.Categories
    .Where(c => db.ExternalResources.Any(er => er.CategoryID == c.CategoryID && er.IsInActivity))
    .Select(c => new { c.CategoryID, c.ParentCategoryID })
    .Union(
        db.Categories
            .Join(cte, c => c.CategoryID, cte => cte.ParentCategoryID, (c, cte) => new { c.CategoryID, c.ParentCategoryID })
    );

var allValidIds = cte.Select(x => x.CategoryID).Distinct().ToList();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:17:07