IQueryable vs IEnumerable: Which Is Faster in C#?
If you're working with C#, LINQ, and Entity Framework Core, you've probably seen code like this:
IQueryable<User> query = dbContext.Users;
IEnumerable<User> users = dbContext.Users;
Both allow you to use LINQ.
Both can filter, sort, and project data.
But they don't work the same way.
And when you're querying a database, choosing the wrong one can have a significant performance impact.
So which is faster?
For database queries,
IQueryableis generally more efficient because it allows the query to be translated and executed on the database.IEnumerableperforms LINQ operations in memory once the data has been loaded.
Let's see why.
The important difference isn't simply that one is "faster."
The real difference is where the work happens.
What is IEnumerable<T>?
IEnumerable<T> represents a sequence that can be enumerated.
For example:
var users = new List<User>
{
new User { Id = 1, Name = "John" },
new User { Id = 2, Name = "Sarah" },
new User { Id = 3, Name = "David" }
};
IEnumerable<User> result = users
.Where(u => u.Name.StartsWith("S"));
foreach (var user in result)
{
Console.WriteLine(user.Name);
}
The filtering happens in your application.
Conceptually:
List<User>
↓
C# application
↓
Where(...)
↓
Filtered results
There is no database involved.
What is IQueryable<T>?
IQueryable<T> is designed for querying data sources that can translate LINQ expressions into another query language.
Entity Framework Core is a common example.
IQueryable<User> query = dbContext.Users;
var users = query
.Where(u => u.Name.StartsWith("S"))
.ToList();
EF Core can translate the LINQ expression into SQL.
Conceptually:
C# LINQ
↓
Expression Tree
↓
EF Core
↓
SQL
↓
Database
↓
Only matching rows
EF Core's query provider translates LINQ expressions into database-specific queries such as SQL.
The performance difference
Consider a database containing:
1,000,000 users
Suppose you only want users whose name starts with "S".
Approach 1 — filter using IQueryable
var users = dbContext.Users
.Where(u => u.Name.StartsWith("S"))
.ToList();
EF Core can translate the filter into SQL similar to:
SELECT *
FROM Users
WHERE Name LIKE 'S%'
The database performs the filtering.
Only matching rows need to be returned to your application.
Approach 2 — load everything first
Now consider:
var users = dbContext.Users
.ToList()
.Where(u => u.Name.StartsWith("S"));
The important part is:
.ToList()
Once ToList() executes, the database query has already been executed and the results have been loaded into memory.
The subsequent:
.Where(...)
runs against the in-memory collection.
The flow becomes:
Database
↓
1,000,000 rows
↓
Application memory
↓
Where(...)
↓
Matching users
That's potentially much more expensive.
EF Core specifically warns that unnecessary buffering with ToList or ToArray can load all results into memory when further LINQ operations could instead be performed before materialization.
The ToList() performance trap
This is one of the most important things to understand.
Compare:
Better
var users = dbContext.Users
.Where(u => u.IsActive)
.ToList();
with:
Potentially expensive
var users = dbContext.Users
.ToList()
.Where(u => u.IsActive);
They look similar.
They aren't.
First version
Database
↓
WHERE IsActive = 1
↓
Only required rows
↓
Application
Second version
Database
↓
All rows
↓
Application memory
↓
WHERE IsActive
If your table contains millions of rows, this difference can become significant.
So is IQueryable always faster?
No.
This is an important distinction.
You shouldn't think:
IQueryable= fastIEnumerable= slow
That's not correct.
Instead think:
Where is the data, and where should the filtering happen?
If the data is in a database, you generally want the database to do as much filtering as possible.
If the data is already in memory, IEnumerable is perfectly appropriate.
For example:
var numbers = Enumerable.Range(1, 1_000_000);
IEnumerable<int> result = numbers
.Where(x => x % 2 == 0);
There is no database here.
Using IQueryable doesn't magically make this faster.
The real performance rule
A useful rule is:
Filter, sort, project, and paginate before materializing database results whenever the database provider can translate those operations efficiently.
For example:
var users = await dbContext.Users
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.Select(u => new
{
u.Id,
u.Name
})
.Take(100)
.ToListAsync();
This allows EF Core to build a database query that performs the filtering, sorting, projection, and limiting before returning the results.
This is usually much better than loading the entire table and doing everything in C#.
Another important difference: Select()
Consider:
var users = await dbContext.Users
.Select(u => new
{
u.Id,
u.Name
})
.ToListAsync();
You don't need every column from the database.
You're asking for only:
Id
Name
This is another important database optimization: retrieve only the columns you actually need. Microsoft recommends projecting only the required properties when querying efficiently with EF Core.
What happens when you call AsEnumerable()?
This is another common source of confusion.
Consider:
var users = dbContext.Users
.Where(u => u.IsActive)
.AsEnumerable()
.Where(u => SomeCustomMethod(u));
Before AsEnumerable():
.Where(u => u.IsActive)
can be translated by EF Core and executed by the database.
After AsEnumerable():
.Where(u => SomeCustomMethod(u))
is performed in the application.
Conceptually:
Database
↓
WHERE IsActive = 1
↓
Application
↓
SomeCustomMethod()
This can be useful when you deliberately need client-side processing.
But don't accidentally move a large amount of work from the database to your application.
EF Core documents AsEnumerable() and ToList() as ways to explicitly opt into client-side evaluation; AsEnumerable() streams results, while ToList() buffers them in memory.
AsEnumerable() vs ToList()
These are also different.
AsEnumerable()
var users = dbContext.Users
.Where(u => u.IsActive)
.AsEnumerable();
This switches subsequent LINQ operations to LINQ-to-Objects.
It doesn't immediately create a list.
ToList()
var users = dbContext.Users
.Where(u => u.IsActive)
.ToList();
This executes the query and buffers the results into a list.
So:
AsEnumerable()
↓
Switch to IEnumerable
↓
Results can be streamed
ToList()
↓
Execute query
↓
Buffer results in memory
If you don't need to materialize the entire result set, unnecessarily calling ToList() can increase memory usage.
What about IQueryable and deferred execution?
Both IEnumerable and IQueryable can use deferred execution.
For example:
var query = dbContext.Users
.Where(u => u.IsActive);
At this point, you haven't necessarily retrieved the database rows yet.
The query is executed when you enumerate it or materialize it:
var users = query.ToList();
or:
foreach (var user in query)
{
Console.WriteLine(user.Name);
}
This is why IQueryable is useful for dynamically building database queries.
You can keep composing:
var query = dbContext.Users
.Where(u => u.IsActive);
if (!string.IsNullOrEmpty(search))
{
query = query.Where(u => u.Name.Contains(search));
}
query = query.OrderBy(u => u.Name);
var users = await query.ToListAsync();
The final query can then be translated by EF Core and sent to the database.
A common mistake in repository methods
Consider:
public IEnumerable<User> GetUsers()
{
return dbContext.Users;
}
This may hide the fact that the caller can continue composing the query.
For example:
var users = repository.GetUsers()
.Where(u => u.IsActive)
.ToList();
Depending on the return type and implementation, you may lose the ability to compose provider-specific queries efficiently.
If you're deliberately exposing a composable database query, you might instead expose:
public IQueryable<User> GetUsers()
{
return dbContext.Users;
}
However, this doesn't mean every repository should return IQueryable.
There are architectural trade-offs around exposing database query providers from repositories, and the right choice depends on the design of your application.
When should you use IQueryable?
Use IQueryable when:
you're querying a database
the query needs dynamic composition
filtering should happen on the database
you need database-side sorting
you need database-side pagination
you want EF Core to translate LINQ into SQL
Example:
IQueryable<Product> query = dbContext.Products;
query = query.Where(p => p.IsAvailable);
query = query
.OrderBy(p => p.Name)
.Take(50);
var products = await query.ToListAsync();
When should you use IEnumerable?
Use IEnumerable when:
the data is already in memory
you're working with arrays or lists
database translation isn't needed
you intentionally want client-side processing
the dataset is small enough that in-memory processing is appropriate
Example:
var products = new List<Product>();
IEnumerable<Product> result = products
.Where(p => p.IsAvailable)
.OrderBy(p => p.Name);
Which one is faster?
Here's the practical answer:
| Scenario | Better choice |
|---|---|
| Filtering 1 million database rows | IQueryable |
| Filtering 100 in-memory objects | IEnumerable |
| Database pagination | IQueryable |
| Database sorting | IQueryable |
| Database projection | IQueryable |
Processing an existing List<T> | IEnumerable |
| Running custom C# logic that can't be translated | IEnumerable |
| Dynamic EF Core queries | IQueryable |
So don't ask:
"Is IQueryable faster than IEnumerable?"
Ask:
"Where should this operation execute?"
That's the real performance question.
The biggest mistake to avoid
Don't do this:
var users = dbContext.Users
.ToList()
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.Take(100);
You're doing the filtering, sorting, and pagination after loading the data.
Prefer:
var users = await dbContext.Users
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.Take(100)
.ToListAsync();
Now the database can perform those operations before returning the result.
One more performance consideration: indexes
Even using IQueryable doesn't automatically make a query fast.
For example:
var users = await dbContext.Users
.Where(u => u.Email == email)
.ToListAsync();
If the database has to scan a huge table because there isn't an appropriate index, the query can still be slow.
The database's execution plan, indexes, joins, result size, and generated SQL all matter.
Microsoft's EF Core performance guidance emphasizes that appropriate indexes are often one of the most important factors determining query performance.
So:
IQueryable
≠
Automatically fast
Instead:
IQueryable
+
Good SQL
+
Proper indexes
+
Small result set
+
Efficient projection
=
Better database performance
Final takeaway
IQueryable and IEnumerable aren't competing versions of the same thing.
They solve different problems.
Use IQueryable
when your query should be executed by the underlying provider, such as EF Core and a database.
Use IEnumerable
when you're working with data that is already in memory.
The biggest performance mistake is usually not choosing the "wrong interface."
It's materializing data too early.
This:
dbContext.Users
.Where(...)
.OrderBy(...)
.Take(100)
.ToListAsync();
is usually a much better pattern than:
dbContext.Users
.ToList()
.Where(...)
.OrderBy(...)
.Take(100);
When working with large datasets, keeping the query composable until you actually need the results can prevent unnecessary database transfers, CPU work, and memory usage.
Quick rule to remember
Database →
IQueryable→ filter/sort/project/paginate →ToList()
Memory →
IEnumerable→ LINQ-to-Objects
Once you understand where the query executes, the IQueryable vs IEnumerable performance question becomes much easier to answer.
Comments
Post a Comment