[英]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.ProductInCategory
和ProductInCategory.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.