👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
Understanding LINQ Deferred, Immediate, Streaming and Non-Streaming Executions

Understanding LINQ Deferred, Immediate, Streaming and Non-Streaming Executions

LINQ

26 Articles

Improve

In this article, let's learn about how Execution and Operators works in LINQ in .NET.

Note: If you have not done so already, I recommend you read the article on Using LINQ For Each to iterate Collections.

Table of Contents

  1. Introduction
  2. Immediate Execution
  3. Deferred Execution
  4. Streaming Operators
  5. Non-Streaming Operators
  6. Classification Table
  7. Summary

Introduction

LINQ to Objects has two ways of running its standard query operator methods: right away or later on. When using the later option, it can either stream the data or not. Knowing which option you're using can help you understand the results you get from your query, especially if the data is changing or if you're building a query on top of another one. This topic organizes the standard query operators by how they work.

LINQ has four Manners of Execution. They are,

  • Immediate
  • Deferred
  • Streaming
  • Non-Streaming
LINQ Execution Strategy

Understanding LINQ Execution Manners and Operators has the following advantages

  • Better Performance.
  • Less Iterations.

Immediate Execution

Immediate execution in LINQ to Objects means that the data is read and the operation is done right away. Standard query operators that return a single / scalar value always work this way. You can make a query execute immediately by using either Enumerable.ToList() or Enumerable.ToArray() methods. Immediate execution helps you save time by reusing the results of your query instead of running it again. The query results are stored for later use after being retrieved once.

Code Sample - LINQ Immediate Execution

Demo - LINQ Immeduate Execution Demo

Let's try LINQ Immediate Execution with Objects

  • We create an array of integers called numbers with the values { 1, 2, 3, 4, 5 }.
  • Then, we use the LINQ extension method Sum to calculate the sum of all the numbers in the array.
  • Since Sum is an immediate execution operator, the result is calculated immediately and assigned to the sum variable.
  • Click on Immediate Execution Button.
  • Click on reset to try other combination.

Deferred Execution

Deferred execution in LINQ to Objects means that the query doesn't run when it's defined in the code. Instead, it runs only when you actually use the query results, for example in a foreach statement. This means that the query results can change depending on what's in the data source at the time the query is used, not when it's written. If you use the query results multiple times, you might get different results each time. Most standard query operators that return IEnumerable<T> or IOrderedEnumerable<TElement> work this way. Deferred execution lets you reuse queries, and it fetches updated data from the data source each time you iterate through the query results.

Query operators that use deferred execution can also be divided into two types: streaming and non-streaming.

Code Sample - Deferred Execution

Demo - LINQ Deferred Execution Demo

Let's try LINQ Deferred Execution with Objects

  • We create an array of integers called numbers with the values { 1, 2, 3, 4, 5 }.
  • Then, we define a LINQ query using the Where operator to select only the even numbers from the numbers array.
  • Since Where is a deferred execution operator, the query is not actually executed until we enumerate it
  • Next, we change the value of the fourth element in the numbers array to 8. This means that the original source data has changed since we defined the query
  • Finally, we enumerate the query results using a foreach loop and print them to the console using Console.WriteLine. Notice that the query results only include the even numbers in the original array (2 and 4), even though we changed one of the source elements to 8. This is because the query execution is deferred until we enumerate the query, and it uses the updated source data at that time
  • Click on Deferred Execution Button.
  • Click on reset to try other combination.

Query Result:

Streaming Operators

Streaming operators in LINQ to Objects don't need to read all the source data before they start returning elements. Instead, they work on each source element one at a time and return the result immediately if applicable. A streaming operator keeps reading source elements until it can produce a result element. This means that sometimes it has to read more than one source element to make one result element.

Code Sample - LINQ Streaming Operators

Demo - LINQ Streaming Operators Demo

Let's try LINQ Streaming Operators with Objects

  • We create an array of integers called numbers with the values { 1, 2, 3, 4, 5 }.
  • Then, we define a LINQ query using the Where and Select operators.
  • The Where operator is a deferred execution streaming operator that selects only the even numbers from the numbers array.
  • The Select operator is also a deferred execution streaming operator that multiplies each even number by 2.
  • Next, we change the value of the fourth element in the numbers array to 8.
  • This means that the original source data has changed since we defined the query.
  • Finally, we enumerate the query results using a foreach loop and print them to the console using Console.WriteLine.
  • Notice that the query results include the even numbers in the original array (2 and 4), but multiplied by 2 (4 and 8), even though we changed one of the source elements to 8. This is because the query execution is deferred until we enumerate the query, and it uses the updated source data at that time. Also, the query execution is streaming, so it doesn't have to read all the source data before yielding elements.
  • Click on Streaming Execution Button.
  • Click on reset to try other combination.

Query Result:

Non-Streaming Operators

Non-streaming operators in LINQ to Objects need to read all the source data before they can start returning (yielding) any result elements. Sorting and grouping are examples of non-streaming operators. When a non-streaming operator runs, it reads all the source data, puts it into a data structure, performs the operation, and then returns (yields) the resulting elements.

Code Sample - LINQ Non-Streaming Operators

Demo - LINQ Non-Streaming Operators Demo

Let's try LINQ Non-Streaming Operators with Objects

  • We create an array of integers called numbers with the values { 1, 2, 3, 4, 5 }.
  • Then, we define a LINQ query using the OrderBy and Take operators.
  • The OrderBy operator is a deferred execution non-streaming operator that sorts the numbers array in ascending order.
  • The Take operator is also a deferred execution non-streaming operator that selects the first 3 elements of the sorted array.
  • Next, we change the value of the fourth element in the numbers array to 8.
  • This means that the original source data has changed since we defined the query.
  • Finally, we enumerate the query results using a foreach loop and print them to the console using Console.WriteLine.
  • Notice that the query results include the first 3 elements of the sorted numbers array (1, 2, and 3), even though we changed one of the source elements to 8. This is because the query execution is deferred until we enumerate the query, and it uses the updated source data at that time. Also, the query execution is non-streaming, so it has to read all the source data before yielding elements.
  • Click on Non-Streaming Execution Button.
  • Click on reset to try other combination.

Query Result:

Classification Table

The following table classifies each standard query operator method according to its method of execution.

Manner of Execution Query Operators
Immediate Execution Aggregate, All, Any, Average, Cast, Concat, Contains, Count, DefaultIfEmpty, Distinct, ElementAt, ElementAtOrDefault, Except, First, FirstOrDefault, GroupBy (with overload), GroupJoin, Intersect, Join, Last, LastOrDefault, LongCount, Max, Min, OfType, OrderBy, OrderByDescending, Range, Repeat, Reverse, Select, SelectMany, SequenceEqual, Single, SingleOrDefault, Skip, SkipWhile, Sum, Take, TakeWhile, ThenBy, ThenByDescending, ToArray, ToDictionary, ToList, Union, Where, Zip
Deferred Execution - Streaming AsEnumerable, AsQueryable, Cast, Concat, DefaultIfEmpty, Distinct, GroupBy (without overload), Join, OfType, Reverse, Select, SelectMany, Take, TakeWhile, Union, Where
Deferred Execution - Non-Streaming Aggregate, All, Any, Average, Contains, Count, ElementAt, ElementAtOrDefault, Except, First, FirstOrDefault, GroupBy (with overload), GroupJoin, Intersect, Last, LastOrDefault, LongCount, Max, Min, OrderBy, OrderByDescending, Single, SingleOrDefault, Skip, SkipWhile, Sum, ThenBy, ThenByDescending, ToArray, ToDictionary, ToList, Zip

Summary

In this article we learn't how LINQ Execution and Operators work. We saw how Deferred and Immediate execution works along with Streaming and Non-Streaming operators. We learnt the advantages of deferred execution and how to use yield keyword in streaming operations with a live demo. With this I'm completing my LINQ series. Feel free to checkout LINQ learning path in LINQ Channel.

👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
  • LINQ
  • Deferred
  • Immediate
  • Streaming
  • Non-Streaming
  • Execution
  • Operators