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

.NET中如何返回指定分类下所有子分类的商品

嵌套分类商品查询功能实现

需求说明

点击父分类(如「服饰」)时,返回该分类及其所有多级子分类下的全部商品。

现有分类模型

[Key]
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
[ForeignKey("parentID")]
public Kategorija parentCategory { get; set; }
public List<Kategorija> Subcategories { get; set; }
[JsonIgnore]
public List<Proizvod> Proizvodi { get; set; }

现有分类数据接口

[Route("PreuzmiKategorije")]
[HttpGet]
public async Task<ActionResult> preuzmiKategorije()
{
    try
    {
        var kategorije = await Context.Kategorijas.Include(c => c.Subcategories).ToListAsync();
        List<Kategorija> result = new List<Kategorija>();
        List<Kategorija> Rez = new List<Kategorija>();
        HashSet<int> processedCategoryIds = new HashSet<int>();

        foreach (var category in kategorije)
        {
            if (!processedCategoryIds.Contains(category.Id))
            {
                result.Add(category);
                processedCategoryIds.Add(category.Id);

                AddSubcategoriesRecursive(category.Subcategories, result, processedCategoryIds);
            }
        }

        foreach(var res in result)
        {
            if(res.parentCategory == null)
            {
                Rez.Add(res);
            }
        }
        return Ok(Rez);
    }
    catch(Exception e)
    {
        return BadRequest(e.Message);
    }
}

private void AddSubcategoriesRecursive(IEnumerable<Kategorija> subcategories, List<Kategorija> result, HashSet<int> processedCategoryIds)
{
    foreach (var subcategory in subcategories)
    {
        if (!processedCategoryIds.Contains(subcategory.Id))
        {
            result.Add(subcategory);
            processedCategoryIds.Add(subcategory.Id);

            AddSubcategoriesRecursive(subcategory.Subcategories, result, processedCategoryIds);
        }
    }
}

问题代码(商品查询接口)

[Route("PreuzmiProizvodePodkategorija")]
[HttpGet]
public async Task<ActionResult> PreuzmiProizvodePodkategorija(string kategorija)
{
    try
    {
        var kategorijaZaPretragu = await context.Kategorijas
            .Include(c => c.Subcategories)
            .Include(c => c.Proizvodi)
            .Where(c => c.Name == kategorija).FirstOrDefaultAsync();

        if (kategorijaZaPretragu == null)
        {
            return NotFound();
        }

        var proizvodi = new List<Proizvod>();
        DodajProizvodePodkategorija(kategorijaZaPretragu.Subcategories, proizvodi);

        return Ok(proizvodi);
    }
    catch (Exception e)
    {
        return BadRequest(e.Message);
    }
}

private void DodajProizvodePodkategorija(IEnumerable<Kategorija> subkategorije, List<Proizvod> proizvodi)
{
    foreach (var subkategorija in subkategorije)
    {
        proizvodi.AddRange(subkategorija.Proizvodi);

        DodajProizvodePodkategorija(subkategorija.Subcategories, proizvodi);
    }
}

修正方案

问题分析

  1. 未加载深层子分类的商品:原查询仅Include了第一层子分类和商品,深层子分类的Proizvodi未被加载,导致递归时无法获取深层商品。
  2. 未包含父分类自身的商品:原逻辑只遍历子分类的商品,若父分类本身有商品会被遗漏。
  3. 递归加载效率较低:通过EF递归加载嵌套分类会产生多次查询,性能较差。

方案一:优化EF查询并修复递归逻辑

如果需要保留递归加载的方式,需确保所有层级的子分类和商品都被正确加载,同时加入父分类自身的商品:

[Route("PreuzmiProizvodePodkategorija")]
[HttpGet]
public async Task<ActionResult> PreuzmiProizvodePodkategorija(string kategorija)
{
    try
    {
        // 使用递归Include加载所有层级的子分类及其商品
        var kategorijaZaPretragu = await context.Kategorijas
            .Include(c => c.Proizvodi)
            .Include(c => c.Subcategories)
                .ThenInclude(sc => sc.Proizvodi)
            .Include(c => c.Subcategories)
                .ThenInclude(sc => sc.Subcategories)
                    .ThenInclude(ssc => ssc.Proizvodi)
            // 如果分类层级更深,需继续添加ThenInclude,或启用EF Core延迟加载
            .Where(c => c.Name == kategorija).FirstOrDefaultAsync();

        if (kategorijaZaPretragu == null)
        {
            return NotFound();
        }

        var proizvodi = new List<Proizvod>();
        // 先添加父分类自身的商品
        proizvodi.AddRange(kategorijaZaPretragu.Proizvodi);
        // 递归添加所有子分类的商品
        DodajProizvodePodkategorija(kategorijaZaPretragu.Subcategories, proizvodi);

        return Ok(proizvodi);
    }
    catch (Exception e)
    {
        return BadRequest(e.Message);
    }
}

private void DodajProizvodePodkategorija(IEnumerable<Kategorija> subkategorije, List<Proizvod> proizvodi)
{
    foreach (var subkategorija in subkategorije)
    {
        proizvodi.AddRange(subkategorija.Proizvodi);
        // 递归处理子分类的子分类
        DodajProizvodePodkategorija(subkategorija.Subcategories, proizvodi);
    }
}

方案二:更高效的批量查询方式(推荐)

先通过递归获取该分类下的所有子分类ID(包括自身),然后直接查询这些分类下的所有商品,避免多次EF查询:

[Route("PreuzmiProizvodePodkategorija")]
[HttpGet]
public async Task<ActionResult> PreuzmiProizvodePodkategorija(string kategorija)
{
    try
    {
        var targetCategory = await context.Kategorijas
            .FirstOrDefaultAsync(c => c.Name == kategorija);

        if (targetCategory == null)
        {
            return NotFound();
        }

        // 获取所有相关分类ID(自身+所有多级子分类)
        var categoryIds = new HashSet<int>();
        GetAllCategoryIds(targetCategory.Id, categoryIds);

        // 批量查询这些分类下的所有商品
        var proizvodi = await context.Proizvodis
            .Where(p => categoryIds.Contains(p.KategorijaId)) // 假设Proizvod有KategorijaId外键
            .ToListAsync();

        return Ok(proizvodi);
    }
    catch (Exception e)
    {
        return BadRequest(e.Message);
    }
}

// 递归获取所有子分类ID
private void GetAllCategoryIds(int categoryId, HashSet<int> categoryIds)
{
    if (categoryIds.Contains(categoryId))
        return;

    categoryIds.Add(categoryId);

    // 查询当前分类的子分类
    var subcategories = context.Kategorijas
        .Where(c => c.parentID == categoryId)
        .Select(c => c.Id)
        .ToList();

    foreach (var subId in subcategories)
    {
        GetAllCategoryIds(subId, categoryIds);
    }
}

补充说明

  • 方案二性能更优,尤其当分类层级较深或商品数量较多时,避免了EF嵌套加载的开销。
  • 若使用方案一且分类层级不确定,建议开启EF Core延迟加载(需在DbContext配置中启用,且导航属性设为virtual),深层子分类的商品会在访问时自动加载。
  • 分类名称可能重复,建议改为通过分类ID查询,避免同名分类导致的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 21:49:57