ZetCode

C# LINQ Deferred Execution

last modified May 14, 2025

This tutorial explores LINQ deferred execution in C#, a key concept that controls when queries are evaluated. You'll learn how deferred execution works, its benefits, and how to manage query execution effectively.

Deferred execution, also known as lazy evaluation, delays LINQ query execution until the results are needed. This approach allows for flexible query composition, optimization, and efficient resource usage, especially when working with large datasets or databases.

Key benefits of deferred execution:

However, deferred execution requires careful handling, as queries are re-evaluated each time they are enumerated, which can lead to unexpected results or performance issues if not managed properly.

Basic Example

A simple example demonstrates how deferred execution delays query evaluation until enumeration.

Program.cs
List<int> numbers = [1, 2, 3, 4, 5];

// Query definition - not executed yet
var query = from n in numbers
            where n % 2 == 0
            select n;

Console.WriteLine("Query created");

// Modify the data source
numbers.Add(6);
numbers.Add(7);
numbers.Add(8);

// Query execution happens here
Console.WriteLine("Query results:");
foreach (var num in query)
{
    Console.WriteLine(num);
}

The query is defined but not executed until the foreach loop. Modifications to numbers before enumeration affect the results.

var query = from n in numbers
            where n % 2 == 0
            select n;

This LINQ query creates an IQueryable object that represents the query logic but does not execute it immediately.

$ dotnet run
Query created
Query results:
2
4
6
8

Deferred vs Immediate Execution

LINQ methods are divided into those that use deferred execution and those that trigger immediate execution.

Program.cs
List<string> fruits = ["apple", "banana", "cherry", "date"];

// Deferred execution
var deferredQuery = fruits.Where(f => f.Length > 5);

// Immediate execution
var immediateResult = fruits.Count(f => f.Length > 5);

fruits.Add("elderberry");
fruits.Add("fig");

Console.WriteLine("Deferred query results:");
foreach (var fruit in deferredQuery)
{
    Console.WriteLine(fruit);
}

Console.WriteLine($"\nImmediate count result: {immediateResult}");

Methods like Where and Select defer execution, while Count, ToList, and ToArray force immediate execution.

$ dotnet run
Deferred query results:
banana
cherry
elderberry

Immediate count result: 2

Multiple Enumeration Considerations

Deferred queries are re-evaluated each time they are enumerated, which can lead to different results if the data source or logic changes.

Program.cs
Random random = new();
var numbers = Enumerable.Range(1, 5).Select(n => random.Next(1, 100));

var query = numbers.Where(n => n % 2 == 0);

Console.WriteLine("First enumeration:");
foreach (var num in query) Console.WriteLine(num);

Console.WriteLine("\nSecond enumeration:");
foreach (var num in query) Console.WriteLine(num);

Each enumeration generates new random numbers, producing different results.

$ dotnet run
First enumeration:
42
88

Second enumeration:
56
24

Forcing Immediate Execution

Use conversion methods like ToList or ToArray to force immediate query execution and cache results.

Program.cs
List<int> data = [10, 20, 30, 40, 50];

// Deferred execution
var deferredQuery = data.Select(n => {
    Console.WriteLine($"Processing {n}");
    return n * 2;
});

// Force immediate execution
var immediateList = deferredQuery.ToList();

Console.WriteLine("\nResults:");
foreach (var num in immediateList)
{
    Console.WriteLine(num);
}

ToList executes the query immediately, and subsequent enumerations use the cached results.

$ dotnet run
Processing 10
Processing 20
Processing 30
Processing 40
Processing 50


Results:
20
40
60
80
100

Chaining and Deferred Execution

LINQ method chains maintain deferred execution until the final enumeration, allowing complex query composition.

Program.cs
List<string> words = ["sky", "blue", "cloud", "forest", "ocean"];

var query = words
    .Where(w => w.Length > 3)
    .OrderBy(w => w)
    .Select(w => w.ToUpper());

words.Add("river");

foreach (var word in query)
{
    Console.WriteLine(word);
}

The entire chain is evaluated only during enumeration, including the newly added "river".

$ dotnet run
BLUE
CLOUD
FOREST
OCEAN
RIVER

Deferred Execution with Nested Collections

Deferred execution is useful for processing nested collections, such as filtering inner collections dynamically.

Program.cs
var departments = new[]
{
    new { Name = "HR", Employees = new[] { "Alice", "Bob" } },
    new { Name = "IT", Employees = new[] { "Charlie", "Dave" } }
};

var query = from dept in departments
            let activeEmployees = dept.Employees.Where(e => e.Length > 4)
            where activeEmployees.Any()
            select new { dept.Name, ActiveEmployees = activeEmployees };

foreach (var dept in query)
{
    Console.WriteLine($"{dept.Name}: {string.Join(", ", dept.ActiveEmployees)}");
}

The inner collection is filtered only when the query is enumerated, reflecting the latest data.

$ dotnet run
HR: Alice
IT: Charlie

Deferred Execution in Database Queries

Deferred execution is critical for database queries, minimizing round-trips to the database.

Program.cs
var dbQuery = dbContext.Products
    .Where(p => p.Price > 100)
    .OrderBy(p => p.Name);

// Additional filtering can be added later
if (categoryFilter != null)
{
    dbQuery = dbQuery.Where(p => p.Category == categoryFilter);
}

// Query executes only when materialized
var results = dbQuery.ToList();

The query is built incrementally and sent to the database only when ToList is called.

Best Practices for Deferred Execution

To use deferred execution effectively:

Source

Deferred Execution vs. Lazy Evaluation in LINQ

This tutorial covered the essentials of LINQ deferred execution in C#, including its mechanics, benefits, and practical applications.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.