Understanding LINQ Deferred, Immediate, Streaming and Non-Streaming Executions
LINQ
26 Articles
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
- Introduction
- Immediate Execution
- Deferred Execution
- Streaming Operators
- Non-Streaming Operators
- Classification Table
- 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
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
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
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
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
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.