BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

LINQ
.NET 3.5+

Simple LINQ Queries

The second part of the LINQ to Objects tutorial describes basic querying using LINQ features. The article discusses the "Where" standard query operator, known as the restriction operator, and the equivalent query expression syntax.

Restrictions and Query Expression Syntax

In this section we will recreate the queries from above using the Query Expression syntax. This is the more natural syntax that some developers prefer to use to generate queries. The two syntaxes are equally valid and both should be considered for their suitability according to the task being undertaken. For example, you may decide to use the extension methods when working with other C# developers and to use the query syntax when working closely with SQL users.

The basic form of the query expression syntax is as follows:

var results =
    from range-variable in data-source
    where predicate
    select projection;

In this syntax, the results element receives the results of the query. As with the standard query operators, the result is usually an IEnumerable<T> object that contains all of the information required to generate the query's results. The results are usually generated using deferred execution when they are needed. Here I have declared the type for the results implicitly using the var keyword.

The from clause includes two important elements. The data source specifies the data structure that contains the items to be queried. The range variable is a variable that becomes available to the rest of the query to represent each item. In this article we will see how it is used to specify the predicate that filters the results. In future articles we will see how it can be used to determine the structure of the returned data and to control other operations, such as sorting.

The where clause provides the restrictions to apply when filtering the data source. The predicate is a Boolean expression that is very similar to the lambda expression used in earlier examples. The syntax differs slightly as no lambda operator or parameters are supplied.

The final section in this simple syntax is the select clause. The projection element determines which elements of the data source are returned. In this article we will return the entire object for each valid result. In the next article we will see how other projections are possible.

To demonstrate the syntax, we can recreate the earlier examples. The first example queried an array of strings containing the names of colours. Only colours with a string length of five or greater were included in the results. This can be expressed as a query as follows:

var longNames =
    from c in colours
    where c.Length >= 5
    select c;

In the above code, the results are returned in the longNames variable. The range variable is named "c". It is common to see short or single-character range variable names, though longer names are acceptable and can improve code clarity. The where clause specifies that the length of c must be greater than or equal to five for an item from the data source, colours, to be included in the results. The select clause specifies that for each result, the "c" object will be returned.

Let's recreate the second sample, which retrieved all employees from a list if their job title included the word, "Developer":

var developers =
    from e in employees
    where e.Title.Contains("Developer")
    select e;

Again, the syntax is reasonably simple. You can almost read the query as plain English: For each employee, if the Title property contains the string, "Developer", add it to the results.

To complete the conversions, we can express the more complex employee query using the query syntax. In this case Boolean operators were used to combine the predicate's conditions:

var developers =
    from e in employees
    where (e.Title.Contains("Developer") && e.Salary > 35000)
       || (!e.Title.Contains("Developer") && e.Salary < 25000)
    select e;

Simplifying Queries with the Let Clause

The query expression syntax can become long-winded, especially when using complex queries. Even in the previous sample, there is duplication because employees' titles are checked twice to see if they include the word, "Developer". Luckily, LINQ queries include a clause that can simplify such queries. The let clause allows you to define additional range variables that are calculated once for each item in the data source. We can improve the efficiency of our query by introducing a range variable that pre-calculates a Boolean value that specifies whether the employee is a developer:

var developers =
    from e in employees
    let isDeveloper = e.Title.Contains("Developer")
    where (isDeveloper && e.Salary > 35000)
       || (!isDeveloper && e.Salary < 25000)
    select e;

The let clause in the above query reduces the complexity of the query and lowers the number of calculations that are performed. In very complex queries this can be extremely useful.

Querying IEnumerable Collections

So far we have executed queries against generic collections, which specify and enforce the type of each item in the data source. This makes type inference possible for lambda expressions and range variables. Sometimes you will need to query collections or data structures that implement the IEnumerable interface but not its generic counterpart. In these cases it is not possible to guarantee the type of each item and type inference fails.

When using standard query operators, you can resolve the problem by using the Cast method. This is an extension method to the IEnumerable interface that allows you to specify the type of the data items and obtain an IEnumerable<T>. The Where method can then use the results of Cast, knowing the type that is to be queried. The Cast method uses deferred execution so is applied only when its results are used. This means that the Cast and Where operators may be applied as a single operation. The query will fail if any element in the data source cannot be converted to the specified type.

The following shows the Case and Where methods used together to query an ArrayList.

var colours = new ArrayList(new string[]
    { "Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet" });
var longNames = colours.Cast<string>().Where(c => c.Length >= 5);

Specifying the type when using query expression syntax is equally simple. Instead of using a Cast operator, the type to assume for each data source item is provided with the range variable. Note the use of the "string" data type before the "c" range variable in the following example. Without this the code would not compile.

var longNames =
    from string c in colours
    where c.Length >= 5
    select c;
21 June 2010