IQueryable.Where.ToList VS IQueryable.ToList.Where,哪个查询在性能上更好?

IQueryable.Where.ToList VS IQueryable.ToList.Where, which query is better in terms of performance?

我有两个查询,都产生相同的结果,但我想知道哪个更有效率。下面是查询,我只写了查询子句。两个查询的内部条件相同。

  1. IQueryable().Where().ToList();
  2. IQueryable().ToList().Where();

最后我尝试了下面的代码,它显示 "IQueryable().ToList().Where();" 更好。有几个问题我不明白: 1. 没有看到我下面的临时代码,哪个查询效率更高? 2. 据我所知,IQueryable 非常适合查询远程数据。那么,是不是应该先过滤掉item,然后使用ToList,这样我们就不需要对不相关的item执行ToList函数了呢? (如果是这种情况,那么为什么下面的代码说查询 2 更有效?)

Stopwatch st1 = new Stopwatch();
Stopwatch st2 = new Stopwatch();
int counter = 10000;
IEnumerable<Employee> iEmp = null;
IQueryable<Employee> qEmp = null;
BindingList<Employee> bList = new BindingList<Employee>();
for (int i = 1; i <= counter; ++i)
{
    bList.Add(new Employee
    {
        Department = $"Dept - {i}",
        EmployeeID = i,
        EmployeeName = $"Employee - {i}",
        Salary = i + 10000
    });
}

iEmp = bList.AsEnumerable<Employee>();
qEmp = bList.AsQueryable<Employee>();

st1.Start();
var t = qEmp.Where(x => x.EmployeeID % 2 == 0).ToList();
st1.Stop();
Console.WriteLine($"Queryable-Where-ToList: {st1.ElapsedTicks}");

st2.Start();
var t1 = qEmp.ToList().Where(x => x.EmployeeID % 2 == 0);
st2.Stop();
Console.WriteLine($"Queryable-ToList-Where: {st2.ElapsedTicks}");
Console.ReadKey();

你应该使用 IQueryable.Where.ToList。这样过滤将在查询的上下文中发生,而不是先收集结果然后像 IQueryable.ToList.Where 那样过滤内存中的所有结果。

在您使用 BindingList 的示例中,这并不重要,但是 在使用 Entity Framework 之类的东西时很重要。

对于 EF,给定

Id | Name
1  | Daniel
2  | Miranda
3  | Elianna

执行.ToList 将首先返回所有 3 条记录。执行 Where 然后处理内存中的那些对象。

 var list = query.ToList();
 list.Count == 3; // true
 list.Where(i => i.Id % 2 == 0); // Returns only Miranda

但是先把.Where转换成SQL(这里是伪SQL)然后收集结果。

SELECT * FROM Table WHERE Id % 2 = 0

在 C# 中:

 var list = query.Where(i => i.Id % 2 == 0).ToList();
 list.Count == 1; // true, only Miranda

在为什么 2 更快的情况下,您的测试存在缺陷。这些测试不是孤立的 运行。它们在短时间内对相同的数据进行操作,这可能会导致 CPU/runtime/operating 系统为您优化某些内容。

当调用ToList时,您将把所有项目都放入内存中。当您使用 Where 语句过滤此结果时,所有这些过滤都将发生在客户端(可以这么说),在您的情况下是在内存中。但是,当您在 Where-statement before 调用 ToList 之前使用第一种方法时,过滤发生在数据库端,因此应该快得多。

不清楚为什么 versa 方法会更快,但从我的观点来看,你应该尽可能使用 IQueryable 并将结果存入内存(这实际上使它成为 IEnumerable) 完成查询后。

在你的例子中,由于 AsQueryable、ToList 和 Where 的实现,你的测试代码 IQueryable().ToList().Where() 工作得更快。 AsQueryable 只是包装了 BindingList,它是 ICollection,ToList 使用 ICollection 比使用 Where 返回的 IEnumerable 更快。

一般情况下,应该使用 IQueryable 来构建对数据存储的查询,因此在数据库端执行整个查询应该更有效(极少数情况除外)。