简体   繁体   English

LINQ查询以获取递归类别结构和每个类别的乘积

[英]LINQ query to get recursive category structure and products of each category

How can I re-write my query to include all the products of each category in a recursive product category tree? 如何重写查询以将每个类别的所有产品包括在递归产品类别树中? This query gets the categories and their children, so I'm just missing the products: 该查询获取类别及其子级,因此我只缺少产品:

var categories = _context.ProductCategories
                .Include(e => e.Children)
                .Where(e => e.ParentId == null)
                .ToList();

Entity models: 实体模型:

public class ProductCategory
{
    public int Id { get; set; }
    public int SortOrder { get; set; }
    public string Title { get; set; }
    [ForeignKey(nameof(ParentCategory))]
    public int? ParentId { get; set; }
    // Nav.props:
    public ProductCategory ParentCategory { get; set; }
    public ICollection<ProductCategory> Children { get; set; }
    public List<ProductInCategory> ProductInCategory { get; set; }
}

public class ProductInCategory
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public int SortOrder { get; set; }
    public int ProductCategoryId { get; set; }
    // Nav.props:
    public Product Product { get; set; }
    public ProductCategory ProductCategory { get; set; }
}

public class Product
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Info { get; set; }
    public decimal Price { get; set; }
    // Nav.props:
    public List<ProductInCategory> InCategories { get; set; }
}

Viewmodel: 视图模型:

public class ViewModelProductCategory
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Title { get; set; }
    public int SortOrder { get; set; }

    public string ProductCountInfo
    {
        get
        {
            return Products != null && Products.Any() ? Products.Count().ToString() : "0";
        }
    }
    public ViewModelProductCategory ParentCategory { get; set; }
    public IEnumerable<ViewModelProductCategory> Children { get; set; }
    public IEnumerable<ViewModelProduct> Products { get; set; }
}

I'm using AutoMapper to convert the query result to viewmodel. 我正在使用AutoMapper将查询结果转换为viewmodel。

Here is how I'm attempting to render: 这是我尝试渲染的方式:

Index-view: 索引视图:

@model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>

<ul style="list-style:none;padding-left:0px;">
    @Html.Partial("_CategoryAndProductRecursive", Model)
</ul>

_CategoryAndProductRecursive.cshtml: _CategoryAndProductRecursive.cshtml:

@model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>
<ul>
@if (Model != null)
{
    foreach (var item in Model)
    {
    <li>
    @item.Title
        <ul style="list-style:none;">
        @Html.Partial("_CategoryAndProductRecursive.cshtml", item.Children)
        @if (item.Products != null)
        {
            @foreach (var product in item.Products)
            {
                <li>@product.Title</li>
            }
        }
        </ul>
    </li>
    }
}
</ul>

There are actually two independent questions for the EF Core query part. EF核心查询部分实际上有两个独立的问题。

First is how to eager load the ProductCategory.ProductInCategory and ProductInCategory.Product navigation properties. 首先是如何急于加​​载ProductCategory.ProductInCategoryProductInCategory.Product导航属性。 The answer to this is simple - use Include / ThenInclude` as explained in the Loading Related Data - Eager Loading section of the documentation. 答案很简单-使用Include / ThenInclude`,如文档的“ 加载相关数据 - 急切加载”部分所述。

The second is how to do that recursively for ProductCategory.Children . 第二个是如何为ProductCategory.Children递归地执行该操作。 The trick is to force loading the whole ProductCategory tree in memory, thus letting EF Core navigation property fixup do the parent/child entity linking, and then filter the root entities. 技巧是强制将整个 ProductCategory树加载到内存中,从而让EF Core导航属性fixup进行父/子实体链接, 然后过滤根实体。

var categories = _context.ProductCategories
    .Include(e => e.ProductInCategory)
        .ThenInclude(e => e.Product)
    .AsEnumerable() // <-- Force full execution (loading) of the above
    .Where(e => e.ParentId == null) // <-- then apply the root filter
    .ToList();

Note that we don't need to include Children with this process. 请注意,在此过程中我们不需要包括Children Just be aware of the fact that some Children collections will be null . 请注意,某些Children集合将为null If that's an issue, just make sure you initialize them in the constructor or with initializer. 如果这是一个问题,只需确保您在构造函数中或使用初始化程序初始化它们即可。

public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();

You could try and use Load instead of Include and loop for a predefined number of times to Load the children each time. 您可以尝试使用Load而不是Include and loop进行预定义的次数来每次加载子代。

https://msdn.microsoft.com/en-us/library/bb896249.aspx https://msdn.microsoft.com/zh-CN/library/bb896249.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM