Write C# LINQ queries to query data

Most queries in the introductory Language Integrated Query (LINQ) documentation are written by using the LINQ declarative query syntax. However, the query syntax must be translated into method calls for the .NET common language runtime (CLR) when the code is compiled. These method calls invoke the standard query operators, which have names such as Where, Select, GroupBy, Join, Max, and Average. You can call them directly by using method syntax instead of query syntax.

Query syntax and method syntax are semantically identical, but query syntax is often simpler and easier to read. Some queries must be expressed as method calls. For example, you must use a method call to express a query that retrieves the number of elements that match a specified condition. You also must use a method call for a query that retrieves the element that has the maximum value in a source sequence. The reference documentation for the standard query operators in the System.Linq namespace generally uses method syntax. You should become familiar with how to use method syntax in queries and in query expressions themselves.

Standard query operator extension methods

The following example shows a simple query expression and the semantically equivalent query written as a method-based query.

int[] numbers = [ 5, 10, 8, 3, 6, 12 ];

//Query syntax:
IEnumerable<int> numQuery1 =
    from num in numbers
    where num % 2 == 0
    orderby num
    select num;

//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

foreach (int i in numQuery1)
{
    Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
    Console.Write(i + " ");
}

The output from the two examples is identical. You can see that the type of the query variable is the same in both forms: IEnumerable<T>.

To understand the method-based query, let's examine it more closely. On the right side of the expression, notice that the where clause is now expressed as an instance method on the numbers object, which has a type of IEnumerable<int>. If you're familiar with the generic IEnumerable<T> interface, you know that it doesn't have a Where method. However, if you invoke the IntelliSense completion list in the Visual Studio IDE, you see not only a Where method, but many other methods such as Select, SelectMany, Join, and Orderby. These methods implement the standard query operators.

Screenshot showing all the standard query operators in Intellisense.

Although it looks as if IEnumerable<T> includes additional methods, it doesn't. The standard query operators are implemented as extension methods. Extensions methods "extend" an existing type; they can be called as if they were instance methods on the type. The standard query operators extend IEnumerable<T> and that is why you can write numbers.Where(...).

To use extension methods, you bring them into scope with using directives. From your application's point of view, an extension method and a regular instance method are the same.

For more information about extension methods, see Extension Methods. For more information about standard query operators, see Standard Query Operators Overview (C#). Some LINQ providers, such as Entity Framework and LINQ to XML, implement their own standard query operators and extension methods for other types besides IEnumerable<T>.

Lambda expressions

In the previous example, notice that the conditional expression (num % 2 == 0) is passed as an in-line argument to the Enumerable.Where method: Where(num => num % 2 == 0). This inline expression is a lambda expression. It's a convenient way to write code that would otherwise have to be written in more cumbersome form. The num on the left of the operator is the input variable, which corresponds to num in the query expression. The compiler can infer the type of num because it knows that numbers is a generic IEnumerable<T> type. The body of the lambda is just the same as the expression in query syntax or in any other C# expression or statement. It can include method calls and other complex logic. The return value is just the expression result. Certain queries can only be expressed in method syntax and some of those require lambda expressions. Lambda expressions are a powerful and flexible tool in your LINQ toolbox.

Composability of queries

In the previous code example, the Enumerable.OrderBy method is invoked by using the dot operator on the call to Where. Where produces a filtered sequence, and then Orderby sorts the sequence produced by Where. Because queries return an IEnumerable, you compose them in method syntax by chaining the method calls together. The compiler does this composition when you write queries using query syntax. Because a query variable doesn't store the results of the query, you can modify it or use it as the basis for a new query at any time, even after you execute it.

The following examples demonstrate some simple LINQ queries by using each approach listed previously.

Note

These queries operate on simple in-memory collections; however, the basic syntax is identical to that used in LINQ to Entities and LINQ to XML.

Example - Query syntax

You write most queries with query syntax to create query expressions. The following example shows three query expressions. The first query expression demonstrates how to filter or restrict results by applying conditions with a where clause. It returns all elements in the source sequence whose values are greater than 7 or less than 3. The second expression demonstrates how to order the returned results. The third expression demonstrates how to group results according to a key. This query returns two groups based on the first letter of the word.

List<int> numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];

// The query variables can also be implicitly typed by using var

// Query #1.
IEnumerable<int> filteringQuery =
    from num in numbers
    where num is < 3 or > 7
    select num;

// Query #2.
IEnumerable<int> orderingQuery =
    from num in numbers
    where num is < 3 or > 7
    orderby num ascending
    select num;

// Query #3.
string[] groupingQuery = ["carrots", "cabbage", "broccoli", "beans", "barley"];
IEnumerable<IGrouping<char, string>> queryFoodGroups =
    from item in groupingQuery
    group item by item[0];

The type of the queries is IEnumerable<T>. All of these queries could be written using var as shown in the following example:

var query = from num in numbers...

In each previous example, the queries don't actually execute until you iterate over the query variable in a foreach statement or other statement.

Example - Method syntax

Some query operations must be expressed as a method call. The most common such methods are those methods that return singleton numeric values, such as Sum, Max, Min, Average, and so on. These methods must always be called last in any query because they return a single value and can't serve as the source for an extra query operation. The following example shows a method call in a query expression:

List<int> numbers1 = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
List<int> numbers2 = [15, 14, 11, 13, 19, 18, 16, 17, 12, 10];

// Query #4.
double average = numbers1.Average();

// Query #5.
IEnumerable<int> concatenationQuery = numbers1.Concat(numbers2);

If the method has System.Action or System.Func<TResult> parameters, these arguments are provided in the form of a lambda expression, as shown in the following example:

// Query #6.
IEnumerable<int> largeNumbersQuery = numbers2.Where(c => c > 15);

In the previous queries, only Query #4 executes immediately, because it returns a single value, and not a generic IEnumerable<T> collection. The method itself uses foreach or similar code in order to compute its value.

Each of the previous queries can be written by using implicit typing with `var``, as shown in the following example:

// var is used for convenience in these queries
double average = numbers1.Average();
var concatenationQuery = numbers1.Concat(numbers2);
var largeNumbersQuery = numbers2.Where(c => c > 15);

Example - Mixed query and method syntax

This example shows how to use method syntax on the results of a query clause. Just enclose the query expression in parentheses, and then apply the dot operator and call the method. In the following example, query #7 returns a count of the numbers whose value is between 3 and 7. In general, however, it's better to use a second variable to store the result of the method call. In this manner, the query is less likely to be confused with the results of the query.

// Query #7.

// Using a query expression with method syntax
var numCount1 = (
    from num in numbers1
    where num is > 3 and < 7
    select num
).Count();

// Better: Create a new variable to store
// the method call result
IEnumerable<int> numbersQuery =
    from num in numbers1
    where num is > 3 and < 7
    select num;

var numCount2 = numbersQuery.Count();

Because Query #7 returns a single value and not a collection, the query executes immediately.

The previous query can be written by using implicit typing with var, as follows:

var numCount = (from num in numbers...

It can be written in method syntax as follows:

var numCount = numbers.Count(n => n is > 3 and < 7);

It can be written by using explicit typing, as follows:

int numCount = numbers.Count(n => n is > 3 and < 7);

See also