Skip to main content

Command Palette

Search for a command to run...

🚀 ToList() is Slowing You Down: LINQ Execution Strategies Every .NET Dev Should Know

Published
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);

More from this blog

CodeWithSai

16 posts