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

如何改写LINQ查询以实现含各分类产品的递归商品分类树?

构建带产品的递归商品分类树解决方案

嘿,我来帮你搞定这个需求!要把现有的分类查询改成包含产品的递归分类树,咱们一步步来:

1. 先明确实体与视图模型结构

首先得确保你的实体和视图模型包含必要的关联属性,我先假设你的模型大概是这样(如果和你的实际结构有出入,调整对应字段就行):

实体模型

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? ParentId { get; set; }
    // 关联父分类
    public virtual Category Parent { get; set; }
    // 关联子分类
    public virtual ICollection<Category> Children { get; set; } = new List<Category>();
    // 分类下的产品
    public virtual ICollection<Product> Products { get; set; } = new List<Product>();
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
    public virtual Category Category { get; set; }
}

视图模型

public class CategoryViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    // 子分类视图模型列表
    public List<CategoryViewModel> Children { get; set; } = new List<CategoryViewModel>();
    // 当前分类下的产品列表
    public List<ProductViewModel> Products { get; set; } = new List<ProductViewModel>();
}

public class ProductViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

2. 改写LINQ查询:加载分类+子分类+产品

如果用Entity Framework Core,你可以用Include + ThenInclude来一次性加载所有关联数据,再递归整理成树形结构;或者用递归查询(CTE)来获取层级数据。这里推荐两种方式:

方式一:EF Core 预加载所有数据,内存中构建树形(适合数据量不大的场景)

// 获取所有分类,同时加载子分类和产品
var allCategories = await _dbContext.Categories
    .Include(c => c.Children)
    .Include(c => c.Products)
    .ToListAsync();

// 筛选出顶级分类(ParentId为null的),并构建树形结构
var rootCategories = allCategories
    .Where(c => c.ParentId == null)
    .ToList();

方式二:用SQL CTE递归查询(适合大数据量,减少内存压力)

如果数据量比较大,可以直接写SQL CTE查询,然后映射到模型:

var recursiveCategoriesQuery = _dbContext.Categories
    .FromSqlRaw(@"
        WITH RecursiveCategories AS (
            SELECT Id, Name, ParentId, 0 as Level
            FROM Categories
            WHERE ParentId IS NULL
            UNION ALL
            SELECT c.Id, c.Name, c.ParentId, rc.Level + 1
            FROM Categories c
            JOIN RecursiveCategories rc ON c.ParentId = rc.Id
        )
        SELECT rc.*, p.*
        FROM RecursiveCategories rc
        LEFT JOIN Products p ON rc.Id = p.CategoryId
    ")
    .AsEnumerable() // 先加载到内存,再整理树形
    .GroupBy(c => c.Id)
    .Select(g => new Category
    {
        Id = g.Key,
        Name = g.First().Name,
        ParentId = g.First().ParentId,
        Products = g.Where(p => p.Id != null).Select(p => new Product { Id = p.Id, Name = p.Name, Price = p.Price }).ToList()
    })
    .ToList();

// 再构建树形结构(筛选ParentId为null的顶级分类)
var rootCategories = recursiveCategoriesQuery.Where(c => c.ParentId == null).ToList();

3. AutoMapper 配置递归映射

要让AutoMapper自动处理递归的分类和产品映射,你需要在Profile里配置:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        // 分类映射:包含子分类和产品
        CreateMap<Category, CategoryViewModel>()
            .ForMember(dest => dest.Children, opt => opt.MapFrom(src => src.Children))
            .ForMember(dest => dest.Products, opt => opt.MapFrom(src => src.Products));
        
        // 产品映射
        CreateMap<Product, ProductViewModel>();
    }
}

这样当你映射顶级分类时,AutoMapper会自动递归映射所有子分类和对应的产品。

4. 控制器逻辑

在控制器里获取数据,用AutoMapper转换,传给视图:

private readonly IMapper _mapper;
private readonly AppDbContext _dbContext;

public CategoryController(AppDbContext dbContext, IMapper mapper)
{
    _dbContext = dbContext;
    _mapper = mapper;
}

public async Task<IActionResult> Index()
{
    // 获取所有分类(带子分类和产品)
    var allCategories = await _dbContext.Categories
        .Include(c => c.Children)
        .Include(c => c.Products)
        .ToListAsync();
    
    // 筛选顶级分类
    var rootCategories = allCategories.Where(c => c.ParentId == null).ToList();
    
    // 用AutoMapper转换为视图模型
    var categoryViewModels = _mapper.Map<List<CategoryViewModel>>(rootCategories);
    
    return View(categoryViewModels);
}

5. 视图渲染:递归局部视图

Index.cshtml(主视图)

@model List<CategoryViewModel>

<h1>商品分类树</h1>

<div class="category-tree">
    @foreach (var category in Model)
    {
        @await Html.PartialAsync("_CategoryAndProductRecursive", category)
    }
</div>

_CategoryAndProductRecursive.cshtml(递归局部视图)

@model CategoryViewModel

<div class="category-node">
    <h3>@Model.Name</h3>
    
    <!-- 渲染当前分类下的产品 -->
    @if (Model.Products.Any())
    {
        <ul class="product-list">
            @foreach (var product in Model.Products)
            {
                <li>@product.Name - @product.Price.ToString("C")</li>
            }
        </ul>
    }
    
    <!-- 递归渲染子分类 -->
    @if (Model.Children.Any())
    {
        <div class="child-categories ml-4">
            @foreach (var childCategory in Model.Children)
            {
                @await Html.PartialAsync("_CategoryAndProductRecursive", childCategory)
            }
        </div>
    }
</div>

这样就能完美构建出包含所有层级分类和对应产品的递归分类树啦!如果你的实体模型有特殊字段,只需要调整对应的映射和视图部分就行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:42:30