DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

5 C# Secrets That Make LINQ Queries 10x Faster (You're Using It Wrong)

5 C# Secrets That Make LINQ Queries 10x Faster (You're Using It Wrong)

5 C# Secrets That Make LINQ Queries 10x Faster (You're Using It Wrong)

Cristian Sifuentes\
Senior .NET Engineer · 2026 Edition


TL;DR

LINQ is not slow. Your mental model of LINQ is.

The difference between expressive LINQ and optimized LINQ is
understanding:

  • Enumeration cost
  • Allocation pressure
  • Short-circuiting semantics
  • Projection order
  • Materialization strategy

Secret 1 --- The Multi‑Enumeration Tax

var expensiveQuery = allUsers
    .Where(u => u.IsActive)
    .Select(u => new UserDto(u.Id, u.Name, u.CalculateScore()));
Enter fullscreen mode Exit fullscreen mode

This query has NOT executed yet.

Now:

Console.WriteLine(expensiveQuery.Count());         
var first = expensiveQuery.FirstOrDefault();      
foreach (var user in expensiveQuery)
{
}
Enter fullscreen mode Exit fullscreen mode

You just executed the same pipeline multiple times.

Fix

var cached = allUsers
    .Where(u => u.IsActive)
    .Select(u => new UserDto(u.Id, u.Name, u.CalculateScore()))
    .ToList();
Enter fullscreen mode Exit fullscreen mode

Enumerate once. Reuse many.


Secret 2 --- Allocation Awareness

LINQ creates enumerators. Enumerators usually allocate.

var result = numbers
    .Where(n => n % 2 == 0)
    .Take(10)
    .ToList();
Enter fullscreen mode Exit fullscreen mode

Hot path? Consider Span.

Span<int> span = numbers;
var results = new List<int>(10);

foreach (ref readonly var n in span)
{
    if ((n & 1) == 0)
    {
        results.Add(n);
        if (results.Count == 10)
            break;
    }
}
Enter fullscreen mode Exit fullscreen mode

Zero iterator allocations. Predictable performance.


Secret 3 --- Filter Before Projection

Wrong:

var dtos = products
    .Select(p => new ProductDto(p.Id, p.Price))
    .Where(dto => dto.Price > 500)
    .ToList();
Enter fullscreen mode Exit fullscreen mode

Correct:

var dtos = products
    .Where(p => p.Price > 500)
    .Select(p => new ProductDto(p.Id, p.Price))
    .ToList();
Enter fullscreen mode Exit fullscreen mode

With EF Core this also reduces SQL payload.


Secret 4 --- Short‑Circuit Properly

Avoid:

var user = users.Where(u => u.Email == email)
                .Take(1)
                .FirstOrDefault();
Enter fullscreen mode Exit fullscreen mode

Use:

var user = users.FirstOrDefault(u => u.Email == email);
Enter fullscreen mode Exit fullscreen mode

Or:

bool exists = users.Any(u => u.Email == email);
Enter fullscreen mode Exit fullscreen mode

Immediate exit. No wrapping.


Secret 5 --- Materialize Intentionally

Wrong:

var count = users.Where(u => u.IsActive).ToList().Count;
Enter fullscreen mode Exit fullscreen mode

Correct:

var count = users.Count(u => u.IsActive);
Enter fullscreen mode Exit fullscreen mode

Need array?

var arr = users.Where(u => u.IsActive).ToArray();
Enter fullscreen mode Exit fullscreen mode

Need dictionary?

var lookup = users.Where(u => u.IsActive)
                  .ToDictionary(u => u.Id);
Enter fullscreen mode Exit fullscreen mode

Pick the final structure directly.


Memory Model Matters

  • IEnumerable<T>{=html} → deferred, heap-driven iteration
  • IQueryable<T>{=html} → expression tree translation
  • Span<T>{=html} → stack-bound, ref-struct, zero alloc

Performance comes from mechanical sympathy.


Senior Rules

  1. Don't re-enumerate.
  2. Filter early.
  3. Project late.
  4. Short-circuit aggressively.
  5. Materialize with intent.

Cristian Sifuentes\
Performance-first .NET Engineer

Top comments (0)