In my first tutorial on LINQ, I have given you the overview of LINQ architecture, the query syntax and the Query Operators we use to build LINQ queries. In this tutorial you will learn one more important concept of LINQ called deferred query evaluation and I will explain you why it is important for writing high performance LINQ queries. I will also cover many deferred query operators available in LINQ.
Table of Contents
What are LINQ Query Operators?
LINQ Query operators are a set of extension methods to perform different operations in the context of LINQ queries. These operators are the heart and soul of the LINQ technology and they are the real elements that make LINQ possible.
The following is the list of standard query operators along with their category:
Category | Operators |
---|---|
Projection | Select, SelectMany |
Filtering | OffType, Where |
Sorting | OrderBy, OrderByDescending, ThenBy, ThenByDescending, Reverse |
Set | Distinct, Except, Intersect, Union |
Concatenation | Concat |
Aggregation | Aggregate, Average, Count, LongCount, Max, Min, Sum |
Element | ElemenatAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault |
Conversion | AsEnumerable, AsQuerable, Cast, OfType, ToArray, ToDictionary, ToList, ToLookup |
Equality | SequenceEqual |
Generation | DefaultifEmpty, Empty, Range, Repeat |
Grouping | GroupBy, ToLookup |
Join | Join, GroupJoin |
Quantifiers | All, Any, Contains |
Partitioning | Skip, SkipWhite, Take, TakeWhite |
What is Deferred Query Evaluation?
In LINQ, The deferred query evaluation means that the LINQ query is not evaluated when it is defined but it is evaluated when it is used. Consider the following query definition.
List items = new List { "one", "two", "three" };
var allItems = from i in items
select i;
foreach (var s in allItems)
{
Console.WriteLine(s);
}
items.Add("four");
foreach (var s in allItems)
{
Console.WriteLine(s);
}
In the above code snippet, I have defined the query in second line. This will select all the items and assign the query results to variable allItems. By running foreach loop you will see the following output.
one
two
three
Then I have added another item “four” in the list and I am looping allItems once again without redefining the query and you will see the following output by running the second loop.
one
two
three
four
The output is different because the query is not evaluated at the time of definition but it is evaluated at the time when you are iterating the list of items using the foreach loop. This means you can define your query once and can apply it multiple times even if the data in your source sequence has been changed. In other words, you will get updated results every time you will iterate your query.
From a logical point of view, a LINQ query define a kind of “query plan” which will not be execute until it is used and which will be executed again and again every time it will be run. However, consider a situation in which you want a safe copy of your query results which remains same even if the source sequence changes. You can do this by using conversion operators such as ToList or ToArray.
Most of the operators in the above table are deferred query operators and they are easy to identify as they all return either IEnumerable or IOrderedEnumerable objects. To give you overview of these operators with their examples I am creating the following employee class in my project with the basic properties.
class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
}
In the main program I am creating the following list of h4objects. I will use this list in all the query operator examples below as input sequence.
List employees = new List();
employees.Add(new Employee { ID = 1, Name = "Peter", Salary = 10000 });
employees.Add(new Employee { ID = 2, Name = "David", Salary = 20000 });
employees.Add(new Employee { ID = 3, Name = "Simon", Salary = 25000 });
employees.Add(new Employee { ID = 4, Name = "John", Salary = 7000 });
employees.Add(new Employee { ID = 5, Name = "Alan", Salary = 3000 });
The remaining tutorial will give you examples of many deferred query operators available in LINQ. As I mentioned above that all the query operators are defined as extension methods in the .NET Framework so I am using extension method syntax in most of the examples below but also giving you simple query syntax where appropriate.
Where operator
The Where operator is used to filter elements into a sequence. For example if you want to filter employees who have salary below 10000 you can write following query.
Extension Method Syntax
var query = employees
.Where(emp => emp.Salary < 10000)
.Select(emp => emp);
LINQ Query Syntax
var query = from emp in employees
where emp.Salary < 10000
select emp;
Select operator
The Select operator returns an output sequence of elements that you select or create dynamically in query. It can also return data types which are different than the types available in source sequence. Consider the following examples:
In the following example the select operator is selecting all the employees from the list.
Extension Method Syntax
var query = employees
.Select(emp => emp);
LINQ Query Syntax
var query = from emp in employees
select emp;
In the following example the select operator is selecting only the names of the employees
Extension Method Syntax
var query = employees
.Select(emp => emp.Name);
LINQ Query Syntax
var query = from emp in employees
select emp.Name;
In the following example the select operator is creating anonymous types on the fly which only have employee ID and Salary properties in it.
Extension Method Syntax
var query = employees
.Select(emp => new { emp.ID, emp.Salary });
LINQ Query Syntax
var query = from emp in employees
select new { emp.ID, emp.Salary };
Take operator
This operator is in the category of partitioning operators and it returns a specified number of elements from the input sequence, starting from the beginning. Consider the following example in which I want to take only the first three employees from the list.
var query = employees.Take(3);
Skip operator
This operator is also in the category of partitioning operators and it skips a specified number of elements from the input sequence starting from the beginning. Consider the following example in which I want to skip the first two employees from the list.
var query = employees.Skip(2);
Concat operator
This operator is in the category of concatenation operators and it allows multiple input sequences of the same type to be concatenated into a single output sequence. Consider the following example in which I am concatenating first and the last employee together.
var first = employees.Take(1);
var last = employees.Skip(4);
var query = first.Concat(last);
OrderBy operator
This operator is in the category of sorting operators and it allows an input sequence to be ordered. Consider the following example where I want all the employees based on the order of their salary.
Extension Method Syntax
var query = employees
.OrderBy(emp => emp.Salary)
.Select(emp => emp);
LINQ Query Syntax
var query = from emp in employees
orderby emp.Salary
select emp;
ThenBy operator
The return type of OrderBy method is IOrderedEnumerable, so it can be ordered again based on any other criteria using ThenBy operator as shown in the example below:
var query = employees
.OrderBy(emp => emp.Salary)
.ThenBy( emp => emp.Name)
.Select(emp => emp);
LINQ also provides OrderByDescending and ThenByDescending operators which perform similar functionality as OrderBy and ThenBy but they order the elements in descending order.
Reverse operator
This operator returns a sequence of the same type as input sequence but in the reverse order. Consider the example below in which I want to obtain list of all employees in reverse order.
var query = employees
.Select( emp => emp)
.Reverse();
Distinct operator
This operator is in the category of set operators and it removes duplicate elements from an input sequence. Consider the following example.
var list1 = employees.Take(5);
var list2 = employees.Skip(0);
var list = list1.Concat(list2);
var query = list.Distinct();
First I concatenate the elements of list1 and list2 into list so that I have some duplicate elements in the input sequence list then I have used distinct operator which will remove all the duplicate elements from the list.
Union operator
This operator is in the category of set operators and it returns a sequence of the set union of two source sequences. Consider the following example.
var list1 = employees.Take(4);
var list2 = employees.Skip(1);
var query = list1.Union(list2);
Intersect operator
This operator is in the category of set operators and it returns the set intersection of two source sequences. Consider the following example.
var list1 = employees.Take(3);
var list2 = employees.Skip(2);
var query = list1.Intersect(list2);
In this tutorial I tried to give you examples of some of the easy deferred query operators available in LINQ. Some operators such as the operators in Join and Grouping category are not covered in this tutorial because they required their own tutorials with detailed explanation and examples.
Awesome Article. Keep it up.
Right topic in the right time…
Great articale to understand the functionality of LINQ
Aoa,
nice tutorial sir,
thanks.
anxiously,waiting for next topic on joins…
Jazak Allah. This tutorial is very good and easy for learning