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.
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
- The first query asks the database to filter and limit before returning rows. The second loads every employee first, then filters locally.
- IQueryable is provider-backed and deferred. IEnumerable describes values you can enumerate; a List<T> is an IEnumerable, so its LINQ operators run in memory.
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.