【问题标题】:IQueryable.Where.ToList VS IQueryable.ToList.Where, which query is better in terms of performance?IQueryable.Where.ToList VS IQueryable.ToList.Where,哪个查询在性能方面更好?
【发布时间】:2017-08-09 04:43:50
【问题描述】:

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

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

最后我尝试了下面的代码,它显示“IQueryable().ToList().Where();”更好。有几个问题我不明白: 1.没有看到我下面的临时代码,哪个查询更有效? 2. 据我所知,IQueryable 非常适合查询远程数据。那么,首先过滤掉项目然后使用 ToList 以便我们不需要对不相关的项目执行 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();

【问题讨论】:

  • 哪个更快?试试看! ericlippert.com/2012/12/17/performance-rant
  • @DavidG 我不知道你有没有看代码,但他已经在用秒表自己看。
  • ToList 在内存中创建一个列表并填充它。如果你之后使用Where,你就无缘无故地创建了这个列表。因为它不再被引用,所以它已准备好被垃圾收集。所以总是使用Where(...).ToList()(如果你需要一个列表)。如果你想强制使用Linq-To-Objects,你应该使用AsEnumerable 而不是ToList
  • 所以IQueryable 基于Expression&lt;T&gt; 并用于数据库(一般来说)。那么,您是否在问是否编译 where 表达式并执行它是否比要求返回所有项目并在内存中应用 where 更快?因为这两件事不能等同
  • @sunil20000 如果您想知道代码在使用 EF 时的行为,请使用 EF 进行测试

标签: c# performance linq iqueryable


【解决方案1】:

您应该使用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/运行时/操作系统为您优化某些内容。

【讨论】:

  • @user4566715 有很多关于如何对代码进行基准测试的示例。
【解决方案2】:

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

目前尚不清楚为什么使用versa 方法会更快,但从我的观点来看,您应该尽可能使用IQueryable,并将结果存入内存(有效地使其成为IEnumerable)重新完成查询。

【讨论】:

    【解决方案3】:

    在您的情况下,由于 AsQueryable、ToList 和 Where 的实现,您的测试代码 IQueryable().ToList().Where() 工作得更快。 AsQueryable 只是包装了 ICollection 的 BindingList,而 ToList 使用 ICollection 比使用 Where 返回的 IEnumerable 更快。

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

    【讨论】:

      猜你喜欢
      • 2011-09-30
      • 2015-06-10
      • 1970-01-01
      • 2019-05-06
      • 2015-03-14
      • 1970-01-01
      • 2011-10-06
      • 1970-01-01
      相关资源
      最近更新 更多