IQueryable vs IEnumerable in EF Core

With IQueryable, EF Core can send the filter and limit to SQL. After ToList, the same LINQ operators run in memory.

Time depends on query planSpace 10 rows vs all rows loadedCode C#
Watch it animatedStep through it, with the running line highlighted. Free with an account.

The idea

A DbSet is an IQueryable: Where and Take build an expression that EF Core can translate to SQL. ToListAsync sends that query to the database. Once ToListAsync has loaded rows into a List, later Where calls use LINQ to Objects and run in your process.

Code

IQueryable<Employee> query = db.Employees;
query = query.Where(e => e.Salary > 100_000);
query = query.Take(10);
var page = await query.ToListAsync();

var all = await db.Employees.ToListAsync();
var localPage = all
    .Where(e => e.Salary > 100_000)
    .Take(10)
    .ToList();

Tips

In the .NET library

Use IQueryable while composing an EF Core query, and materialise with ToListAsync when you need the results. Avoid materialising early unless you really need all rows.