In this article, we’ll focus on the use cases of the LINQ Where Method in C#.

To download the source code for this article, you can visit our GitHub repository.

The LINQ Where Method offers a powerful tool that can manipulate data from various sources ranging from collections of objects in memory to relational database tables and XML files according to a specific criterion. It offers a simple, intuitive syntax similar to SQL, that makes writing queries on collections easier, simpler, maintainable, and more efficient.

To know more about LINQ, read our article on LINQ Basic Concepts in C#

Let’s begin!

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

The Signatures of the LINQ Where Extension Method

The Where() query method has three different signatures, each accepting a collection as input and returning a subset of the original collection.

IEnumerable<T> Collections Filtering

Firstly, .NET provides a Where() method signature allowing filtering of an IEnumerable<T> collection using a predicate. The predicate takes a TSource element and returns a boolean indicating whether or not the element should be included in the filtered result:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate)

Let’s use this Where() method on a collection of integers:

int[] randomNumbers = [1, 5, 23, 11, 7, 2, 9, 8, 3, 6, 4, 20, 15, 0, 18, 13, 10, 33];

var evenNumbers = randomNumbers.Where(x => x % 2 == 0).OrderBy(x => x).ToList();

We apply the filter operator here to retain only the even numbers. If we print the resulting list to the console we see:

0, 2, 4, 6, 8, 10, 18, 20

When working with an in-memory enumerable object, Where() acts as an extension method of the IEnumerable<T> interface.

IEnumerable<T> Collection Indexed Filtering

Secondly, another Where() overload provides access to the index of the current element in the predicate as well as to the element itself:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, int, bool> predicate)

It is important to note that as with all C# collections, the index of an enumerable starts at 0, not 1.

Here, we only want to keep the numbers at odd indices:

var oddNumbers = Enumerable.Range(50, 10).Where((_, index) => index % 2 != 0).ToList();

The result is:

51, 53, 55, 57, 59

Queryable<T> Collections Filtering

The third overload of the Where() method allows filtering IQueryable<T> collections, which represent queries on a data source that may not be in memory, typically like databases. However, in this case, a lambda expression tree forms the predicate:

public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, bool>> predicate)
Refer to our article on Expression Trees in C# to learn more about them.

First of all, to illustrate this overload of the Where operator, we’ll use a database of people. In this model, a person may have zero, one or multiple pets and live in a single address. Additionally, we use Entity Framework Core to access the database. In the context class, we add data to our entities using the OnModelCreating() method. Implementation details are available in the source code. 

Now, let’s query the database to retrieve people who were born in 1974 or later:

IQueryable<Person> queryResult = context.People.Where(p => p.BirthDate.Year >= 1974);
return queryResult.ToList();

Here, we obtain two persons corresponding to our request: 

JACKSON Colleen
RICHARD Dan

We can continuously add operations like OrderBy() or additional Where() clauses to the same IQueryable query. Each operation builds a new expression tree that represents the cumulative query.

The actual SQL query is not generated and sent to the database until we enumerate the IQueryable object, such as by calling ToList(). This ensures that all modifications are included in a single, efficient SQL query, resulting in optimized database access and only returning the filtered results.

Examples of the LINQ Where Method in Action

Let’s explore some use cases of the LINQ Where() method.

Before exploring the examples, we need to remember that the Where() method, like most other LINQ operations, is not executed on construction, but rather on enumeration. This occurs, for instance, when calling ToList() or looping through a foreach. This behavior is known as deferred execution.

Multiple LINQ Where Method Filtering

Firstly, it is possible to chain Where() clauses to refine the filtered data with each iteration.

Let’s gather information on people born before 1974 but who also reside in the city of Nancy, France:

IQueryable<Person> result = context.People
     .Include(person => person.Address)
     .Where(s => s.BirthDate.Year < 1974)
     .Where(s => s.Address.City.Equals("NANCY"));

return result.ToList();

According to our database data, only one person meets these criteria: 

LAWRENCE Dennis

Nested LINQ Where Operators

We also have the option to include an instruction using another Where operator in the predicate of one Where operator:

 IQueryable<Person> PeopleWithAustralianShepherd = context.People
      .Include(p => p.Pets)
      .Where(person => person.Pets.Where(pet => pet.Breed.Contains("Australian"))
      .Any(pet => pet.Name.Equals("Naïa")));

return PeopleWithAustralianShepherd.ToList();

Here, the query returns:

RICHARD Dan

LINQ Where Method With an Expression as Parameter

Finally, let’s rewrite the previous LINQ query, this time using an expression as a parameter of the Where operator:

Expression<Func<Person, bool>> HasAustralianShepherds = 
    p => p.Pets.Any(pet => pet.Breed.Equals("Australian Shepherd") && pet.Name.Equals("Naïa"));
var filteredResult = context.People.Where(HasAustralianShepherds);

return filteredResult.ToList();

Again, our query returns :

RICHARD Dan

Conclusion

In this article, we learned how to filter a sequence of elements according to a boolean condition with the LINQ Where Method. We looked at chaining the Where operator with other LINQ operators to create more complex queries. We also looked at the signatures of the Where operator and explored some use cases.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!