【发布时间】:2018-01-03 13:01:10
【问题描述】:
我之前在 StackOverflow 上的递归产品类别树视图方面得到了一些帮助,这很有效:
实体模型:
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; }
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } //nav. prop to children
public List<ProductInCategory> ProductInCategory { get; set; }
}
控制器:
var categories = _context.ProductCategories.Include(e => e.Children).ToList();
var topLevelCategories = categories.Where(e => e.ParentId == null);
return View(topLevelCategories);
查看:
@if (Model != null)
{
foreach (var item in Model)
{
<li>
@item.Title
<ul>
@Html.Partial("_CategoryRecursive.cshtml", item.Children)
</ul>
</li>
}
}
但是当我尝试将此设置转换为我的视图模型时(并添加一个用于计算每个类别中的产品的属性,以及一个没有任何类别连接的产品列表)...:
视图模型:
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; } // Nav.prop. to parent
public IEnumerable<ViewModelProductCategory> Children { get; set; } // Nav.prop. to children
public List<ViewModelProduct> Products { get; set; } // Products in this category
public List<ViewModelProduct> OrphanProducts { get; set; } // Products with no reference in ProductInCategory
}
控制器:
var VMCategories = _context.ProductCategories
.Include(e => e.Children)
.OrderBy(s => s.SortOrder)
.Where(r => r.ParentId == null) // only need root level categories in the View
.Select(v => new ViewModelProductCategory
{
Id = v.Id,
ParentId = v.ParentId,
Title = v.Title,
SortOrder = v.SortOrder,
// get products without a category:
OrphanProducts = v.ProductInCategory
.Where(o => !_context.ProductsInCategories.Any(pc => o.Id == pc.ProductId))
.Select(orph => new ViewModelProduct
{
Id = orph.Product.Id,
Title = orph.Product.Title,
Price = orph.Product.Price,
Info = orph.Product.Info,
SortOrder = orph.SortOrder
})
.OrderBy(s => s.SortOrder)
.ToList()
})
.ToList();
return View(VMCategories);
查看:
@if (Model != null)
{
foreach (var item in Model)
{
<li>
@item.Title (@item.ProductCountInfo)
<ul>
@Html.Partial("_CategoryRecursive.cshtml", item.Children)
</ul>
</li>
}
}
...它不再起作用了。视图确实呈现,但它只显示根类别。看来我修改后的查询不会获得任何子类别。当我检查查询结果时,Children 属性为空。
编辑
我将使用@Rainman 的解决方案,并将我的查询.Select 更改为包含Children = v.Children,,并因此更改我的视图模型导航属性:
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } //nav. prop to children
我还创建了新的视图模型 CategoryRecursiveModel 并将我的视图更改为:
@model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>
<ul>
@if (Model != null)
{
foreach (var item in Model)
{
<li>
@item.Title (@item.ProductCountInfo)
<ul>
@Html.Partial("_CategoryRecursive.cshtml", new CategoryRecursiveModel
{
Children = item.Children.ToList();
})
</ul>
</li>
}
}
</ul>
现在我遇到了 InvalidOperationException,因为视图需要 ViewModelProductCategory 的 IEnumerable,但接收到 CategoryRecursiveModel。
【问题讨论】:
-
您使用的是 Entity Framework Core 还是 Entity Framework 6?
-
实体框架核心
-
您似乎错过了这样一个事实,即
ToList在这里调用var categories = _context.ProductCategories.Include(e => e.Children).ToList();对您的原始解决方案至关重要,因为它迫使 EF 将整个树加载到内存中并完成繁重的连接工作导航属性(所谓的导航属性修复)。一旦你开始使用投影,你必须自己完成所有Parent/Children属性填充。 -
虽然标记为重复的问题是针对 EF6 的,但同样的原理和解决方案也适用于 EF Core。
标签: c# linq entity-framework-core