Why LINQ?

At a high level, there are number of benefits that may be gained by using LINQ (Language Integrated Query), including:

  • Reduce the amount of code that needs writing.
  • A better understanding of the intent of what the code is doing.
  • Once learned, can use a similar set of LINQ query knowledge against different data sources (in-memory objects, or remote sources such as SQL Server or even Twitter.)
  • Queries can be composed together and built up in stages.
  • Queries offer the safety of compile-time type checking.

Essentially, LINQ enables queries to be treated as first-class citizens in C# and Visual Basic.

The building blocks of LINQ

The two fundamental building blocks of LINQ are the concepts of elements and sequences.

A sequence can be thought of as a list of items, with each item in the list being an element. A sequence is an instance of a class that implements the IEnumerable<T> interface.

If an array of numbers were declared as: int[] fibonacci = {0, 1, 1, 2, 3, 5}; the variable fibonacci represents a sequence with each int in the array being an individual element.

A sequence could be a local sequence of in-memory objects or a remote sequence, like a SQL Server database. In the case of remote data sources (for example SQL Server), these remote sequences also implement the IQueryable<T> interface.

Queries, or more specifically query operators, work on an input sequence and produce some output value. This output value could be a transformed version of the input sequence, i.e. an output sequence or single scalar value such as a count of the number of elements in the input sequence.

Queries that run on local sequences are known as local queries or LINQ-to-objects queries.

There are a whole host of query operators that are implemented as extension methods in the static System.Linq.Enumerable class. This set of query operators are known as the standard query operators. These query operators will be covered in depth in the LINQ Query Operators chapter later in this book.

One important thing to note when using query operators is that they do not alter the input sequence; rather, the query operator will return a new sequence (or a scalar value).

Scalar return values and output sequences

A query operator returns an output sequence or a scalar value. In the following code, the input sequence fibonacci is operated on first by the Count query operator that produces a single scalar value representing the number of elements in the input sequence. Next, the same input sequence is operated on by the Distinct query operator that produces a new output sequence containing the elements from the input sequence, but with duplicate elements removed.

The output from this code produces the following:

Count: 6

Elements in output sequence:

0

1

2

3

5

Deferred execution

The majority of query operators do not execute immediately; their execution is deferred to a later time in the program execution. This means that the query does not execute when it is created, but when it is used or enumerated.

Deferred execution means that the input sequence can be modified after the query is constructed, but before the query is executed. It is only when the query is executed that the input sequence is processed.