C# IEnumerable Multiple Enumeration Error Fix
In this tutorial, you'll learn about C# IEnumerable Multiple Enumeration Error Fix. We cover key concepts, practical examples, and best practices.
Your code iterates over an IEnumerable twice but the second loop is empty:
var items = GetItems(); // returns IEnumerable<string>
Console.WriteLine(items.Count()); // first enumeration
foreach (var item in items) { ... } // second enumeration — empty!
IEnumerable uses deferred execution. The first Count() already consumed the enumeration if the source is a forward-only sequence (like a database cursor or a generator). You need to materialize the sequence before multiple iterations.
Step-by-Step Fix
1. Materialize with ToList() or ToArray()
WRONG — enumerating an IEnumerable multiple times:
IEnumerable<string> lines = File.ReadLines("log.txt");
int count = lines.Count();
string first = lines.First(); // re-opens the file, seeks from start if possible
RIGHT — materialize once:
List<string> lines = File.ReadLines("log.txt").ToList();
int count = lines.Count;
string first = lines[0];
2. Understand deferred execution
WRONG — assuming LINQ queries cache results:
IEnumerable<int> evenNumbers = numbers.Where(n => n % 2 == 0);
Console.WriteLine(evenNumbers.Count());
numbers.Add(6);
Console.WriteLine(evenNumbers.Count()); // different result!
RIGHT — force execution when you need a snapshot:
List<int> evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
Console.WriteLine(evenNumbers.Count);
numbers.Add(6);
Console.WriteLine(evenNumbers.Count); // same result — cached
3. Avoid expensive operations per enumeration
WRONG — each enumeration re-executes the expensive query:
IEnumerable<Order> orders = dbContext.Orders.Where(o => o.Amount > 100);
double average = orders.Average(o => o.Amount);
int count = orders.Count(); // executes another SQL query!
RIGHT — compute everything in one pass:
var orders = dbContext.Orders.Where(o => o.Amount > 100).ToList();
double average = orders.Average(o => o.Amount);
int count = orders.Count;
Or use a single database query:
var stats = dbContext.Orders
.Where(o => o.Amount > 100)
.GroupBy(o => 1)
.Select(g => new { Average = g.Average(o => o.Amount), Count = g.Count() })
.First();
4. Handle forward-only sequences
WRONG — enumerating a forward-only sequence twice:
IEnumerable<string> ReadLines()
{
using var reader = new StreamReader("data.big");
while (!reader.EndOfStream) yield return reader.ReadLine();
}
RIGHT — materialize or restructure:
List<string> ReadAllLines()
{
return File.ReadAllLines("data.big").ToList();
}
Expected output with wrong approach: the second foreach loop yields nothing. With the fix: both loops iterate over all lines.
Prevention
- Materialize IEnumerable with
.ToList()before multiple iterations. - Name deferred collections clearly:
IEnumerable<T>for single-use,IReadOnlyCollection<T>orIReadOnlyList<T>for reusable. - Use
IReadOnlyCollection<T>as return type when the caller needs Count/iterations. - Run Roslyn analyzer for CA1851: "Possible multiple enumeration of IEnumerable."
- For database queries, use
.ToList()to close the connection and cache results.
Common Mistakes with ienumerable error
- Using
foldlinstead offoldl'causing stack overflow on large lists - Forgetting
deriving (Show, Eq)on custom data types needed for debugging - Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable
These mistakes appear frequently in real-world CSHARP code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro