我正在使用EF 6从数据库中获取产品。产品类别在产品上映射为导航属性,数据来自ProductCategory数据透视表。类别的工作方式类似于树(即每个类别都可以有子类别),但只有最具体的产品 - 子类别关系存储在数据透视表中。例如,假设有这样的类别路径:
电子产品>音频>放大器>集成放大器。
作为集成放大器的产品在数据透视表中有一条记录,其产品ID和集成放大器类别ID。
我需要按类别进行过滤,但即使按父类别过滤,产品也应该显示,例如集成放大器应该出现在放大器列表中。所以首先我列出相关的类别ID。 (这涉及对类别表的单独查询,但不需要很长时间。)如果类别过滤器是放大器,则列表是放大器的ID和集成放大器的ID。
问题是,当我包含过滤器时,产品查询需要10到20倍的时间:
List<int> currentCategoryIdAndChildren = BuildCategoryIdList(currentCategoryId);
using (var db = new myContext())
{
var products = db.Products
.Select(p => new Product_PL
{
id = p.ID,
name = p.Name,
description = p.Description,
categories = p.Categories
.Select(c => new Category_PL
{
categoryid = c.ID,
}),
});
// Filter by category
products = products.Where(pl => pl.categories.Any(c => currentCategoryIdAndChildren.Contains(c.categoryid)));
// Other filters, sorting, and paging here
rptProducts.DataSource = products.ToList(); // Database call is made here
rptProducts.DataBind();
}
我希望Any()和Contains()的组合能够通过大量记录快速减速,但我在产品中使用了22个项目,在pl.categories中使用了1-3个项目,以及1-5个currentCategoryIdAndChildren中的项目。令我感到惊讶的是,由于记录太少,它的速度会慢一个数量级。按照这个速度,我最好过滤它的客户端,即使这意味着带回了大量不必要的记录。
有什么我想念的吗?还有另一种方法吗?
更新:Express Profiler报告数据库查询本身只需要3毫秒,因此我猜测性能与实体框架的工作方式有关。当然,它是第一次运行LINQ时速度最慢(我知道它需要编译查询),但在后续调用时它仍然相对较慢。
答案 0 :(得分:4)
我尝试了很多不同的东西,最后找到了解决方案。
我认为当EF将Contains()转换为SQL查询时,主要的减速发生了。然而,最引人注目的是它似乎没有缓存查询。从我可以收集到的,这是因为类别ID列表(currentCategoryIdAndChildren)是在EF之外生成的,所以它假设每次都不同。
通过在LINQKit中使用PredicateBuilder,我能够加快速度。这使我能够更明确地创建逻辑:
var IsInCategory = PredicateBuilder.False<Product_PL>();
foreach (int categoryID in currentCategoryIdAndChildren)
{ IsInCategory = IsInCategory.Or(pl => pl.categories.Any(c => categoryID == c.categoryid)); }
products = products.Where(IsInCategory);
这使我的初始查询性能提高了一些,并且随后的查询表现得更好。
答案 1 :(得分:1)
首先尝试过滤掉产品,然后再形成模型(Product_PL和Category_PL):
var filteredProducts = db.Products.Where(p => p.Categories.Any(c => currentCategoryIdAndChildren.Contains(c.ID)))
.Select(p => new Product_PL
{
id = p.ID,
name = p.Name,
description = p.Description,
categories = p.Categories
.Select(c => new Category_PL
{
categoryid = c.ID,
}),
});