【发布时间】:2014-06-07 09:13:25
【问题描述】:
我正在使用 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 毫秒,所以我猜测性能与 Entity Framework 的工作方式有关。当然,第一次运行 LINQ 时它是最慢的(我知道它需要编译查询),但在随后的调用中它仍然相对较慢。
【问题讨论】:
-
您应该在选择的同时进行过滤。当您在第二行调用“products.Where”时,它首先必须枚举 Products。将 Where 子句移到第一个 linq 调用的末尾
-
@DLeh 你确定这是正确的吗?第一个预测实际上并没有实现,所以我希望 SQL 代码生成足够智能。一种检查方法是记录生成的 SQL。
-
我不是很肯定,但这是我会尝试的第一件事。
-
@Dleh 产品在实际访问之前不会被枚举(例如使用 .ToList() 或 foreach)。我已经验证了 SQL 调用直到那时才进行。实际上,我在上面的代码之后还有其他几个条件过滤器以及分页,最后会产生一个巨大的过滤 SQL 调用。但我会尝试移动 Where 只是为了好玩。
-
整个操作需要多长时间(不仅仅是 SQL 查询)?如果您只调用 ToList 而不将其分配为 DataSource 有什么区别?
标签: c# linq entity-framework