πŸš€ ToList() is Slowing You Down: LINQ Execution Strategies Every .NET Dev Should Know

PriyaPriya
4 min read

"It works… but it's slow."
If you’re building .NET apps with Entity Framework Core, the way you use ToList() might be silently sabotaging performance β€” especially on large datasets.


⚑ The Common Misuse of ToList()

var users = dbContext.Users.ToList();

var activeUsers = users.Where(u => u.IsActive).ToList(); // ❌ Filters in memory

This looks fine β€” but it causes your app to:

  • Fetch all records from the database

  • Filter them in memory, not in SQL

  • Waste memory, increase latency, and break scalability


πŸ” LINQ Execution: Deferred vs Immediate

πŸ” Deferred ExecutionπŸ“Œ Immediate Execution
Query is composed but not run until neededQuery runs instantly
Methods: Where, Select, OrderByMethods: ToList, Count, First
Enables SQL optimization via EF CoreBreaks SQL translation, forces in-memory

🧠 Real-world analogy:

Deferred execution is like preparing a shopping list. You don’t go shopping until you check out.
Immediate execution is like buying something every time you add it to the list.


🧠 How .ToList() Breaks EF Core’s Magic

Entity Framework Core uses IQueryable and expression trees to build SQL from LINQ.

Calling .ToList():

  • Ends that query-building chain

  • Materializes results into memory

  • Turns any further operations into pure C# LINQ, losing database optimization

Example:

// ❌ Inefficient: Filters AFTER SQL

var users = dbContext.Users.ToList()

.Where(u => u.IsActive); // runs in memory

// βœ… Efficient: Filters IN SQL

var users = dbContext.Users

.Where(u => u.IsActive)

.ToList(); // runs in SQL


⚠️ Bonus Trap: Beware .AsEnumerable()

var result = dbContext.Users

.AsEnumerable()

.Where(u => u.IsActive); // ❌ Forces in-memory LINQ

πŸ”΄ Danger: .AsEnumerable() switches the context from IQueryable to IEnumerable. That means:

  • LINQ runs in memory, not SQL

  • You lose translation to SQL

  • You risk performance degradation or runtime exceptions (e.g., trying to translate non-translatable C# code)

Use it only when you know why you're switching to in-memory logic.


πŸ“ˆ Performance Comparison

Imagine querying a table with 100,000 records, needing only 10,000 active ones.

Query StyleSQL QueryTimeMemory Usage
❌ ToList() then WhereSELECT * FROM Users~950ms120MB
βœ… Where() then ToList()SELECT * FROM Users WHERE IsActive = 1~120ms8MB

πŸ“Š Visual Query Flow

[EF LINQ Query]

↓

[Where clause on IQueryable]

↓

[SQL Generated by EF Core]

↓

[Filtered results returned]

🟒 Keep queries composed with .Where, .Select, .OrderBy
πŸ”΄ Avoid terminating early with .ToList() unless absolutely necessary


βœ… Best Practice Patterns

PracticeπŸ”₯ VerdictWhy
Filtering after .ToList()πŸ”΄ BadRuns in memory
Filtering before .ToList()🟒 GoodExecutes in SQL
Projecting specific fields with .Select()🟒 GoodReduces load
Using .AsEnumerable() too earlyπŸ”΄ BadLoses SQL translation
Using .ToList() for disconnected logic🟑 OkaySafe when needed intentionally

πŸ›‘ When Should You Use .ToList()?

  • βœ… When you want to break from IQueryable for disconnected work

  • βœ… Before using LINQ methods not supported in SQL (e.g., custom C# functions)

  • βœ… For batch updates, logging, or JSON serialization

But always place it after all SQL-translateable filters.


πŸ§ͺ Code Comparison

πŸ”΄ Bad:

var products = dbContext.Products.ToList()

.Where(p => p.Price > 1000);

🟒 Good:

var products = dbContext.Products

.Where(p => p.Price > 1000)

.ToList();

🟒 Even Better:

var productSummaries = dbContext.Products

.Where(p => p.Price > 1000)

.Select(p => new { p.Id, p.Name, p.Price })

.ToList();


πŸ“š Want to Go Deeper?

You’ll love these upcoming blog posts:

  • πŸ”— LINQ Gotchas in EF Core: Client-Eval Traps and Workarounds

  • πŸ”— Batching vs Streaming Queries in .NET & EF Core

  • πŸ”— The Hidden Cost of .Include(): When to Load Data Lazily


🎯 Final Thoughts

ToList() isn’t evil β€” it’s just eager.

In performance-sensitive apps, especially with large datasets or live databases:

βœ… Let EF Core do the heavy lifting
βœ… Think before you list
βœ… Always profile queries before pushing to production


🧾 TL;DR:

// βœ… DO

dbContext.Users.Where(u => u.IsActive).ToList();

// ❌ DON'T

dbContext.Users.ToList().Where(u => u.IsActive);

0
Subscribe to my newsletter

Read articles from Priya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Priya
Priya