Back to Blog
C#

C# LINQ Multiple Where Conditions: Chaining vs &&

c# linq multiple where conditions: Learn how to apply multiple Where conditions in C# LINQ, compare chaining vs &&, and understand deferred execution and performance.

LINQC#FilteringIQueryableIEnumerableDeferred Execution
Illustration of multiple LINQ Where filters applied to a collection in C#

c# linq multiple where conditions requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you need to filter a collection by several conditions in C#, you can either chain multiple Where calls or combine the conditions with && inside a single Where. Both approaches produce the same result for in-memory collections, but they differ in readability and in how they behave with IQueryable providers.

Combining Conditions with Multiple Where Calls

The simplest way to apply multiple filters is to call Where repeatedly:

var adults = people .Where(p => p.Age >= 18) .Where(p => p.Name.StartsWith("A"));

Each Where adds another predicate, and the final sequence contains only elements that satisfy every predicate. This is equivalent to a logical AND across all conditions.

The order of the Where calls matters for in-memory collections because each filter is applied sequentially. The second Where operates only on the elements that passed the first filter. This can reduce the number of elements processed by later filters, which is useful when one condition is much more selective than the others.

Single Where with Logical Operators

You can achieve the same result with a single Where and the && operator:

var adults = people .Where(p => p.Age >= 18 && p.Name.StartsWith("A"));

This is often more readable when the conditions are short and closely related. It also makes the intent clearer: the predicate is a single expression that must be true for the element to be included.

There is no functional difference between the two approaches for IEnumerable<T>. The compiler generates the same delegate, and the runtime behavior is identical. The choice is mostly about style and maintainability.

Deferred Execution and Order of Filters

LINQ queries are deferred by default. When you write collection.Where(...).Where(...), no iteration happens until you call ToList(), foreach, or another terminal operation. This means the order of Where calls does not affect when the query executes, but it does affect the order in which predicates are evaluated for each element.

For an in-memory collection, each Where creates a new iterator that wraps the previous one. When you enumerate the final sequence, the first Where predicate is evaluated for the first element, and if it passes, the second predicate is evaluated, and so on. If the first predicate is false, the second is never called for that element. This short-circuiting behavior is why putting the most selective filter first can reduce the total number of predicate evaluations.

var result = numbers .Where(n => n % 2 == 0) // evaluates for every element .Where(n => n > 100); // only for even numbers

If the first condition is cheap and eliminates many elements, the second condition is evaluated far fewer times.

Multiple Where on IQueryable vs IEnumerable

The behavior changes when you work with IQueryable<T>, which is common with Entity Framework Core or other LINQ providers that translate expressions to SQL.

For IQueryable, each Where call adds to an expression tree. When the query is executed, the provider translates the entire expression tree into a single SQL statement. Multiple Where calls become AND conditions in the same WHERE clause:

var query = dbContext.People .Where(p => p.Age >= 18) .Where(p => p.Name.StartsWith("A"));

This produces the same SQL as a single Where with &&. The order of the Where calls does not affect the SQL generation or the execution plan in most relational databases. The provider decides the optimal plan based on indexes and statistics.

For in-memory IEnumerable, the order of filters can affect performance because each filter is applied as a separate iteration step. For IQueryable, the provider sees the whole predicate tree and can optimize accordingly.

Handling Null and Complex Conditions

When conditions involve nullable properties or complex logic, a single Where with proper grouping is often clearer:

var result = people .Where(p => p.MiddleName != null && p.MiddleName.Length > 0);

If you need an OR condition, you must use || and parentheses. Chaining Where calls cannot express OR because each Where is an AND. For example, to find people whose first name starts with "A" or whose last name starts with "B", you need:

var result = people .Where(p => p.FirstName.StartsWith("A") || p.LastName.StartsWith("B"));

If you try to split this into two Where calls, you get an AND instead:

// This is wrong: requires both conditions to be true var result = people .Where(p => p.FirstName.StartsWith("A")) .Where(p => p.LastName.StartsWith("B"));

This is a common mistake when developers assume that multiple Where calls behave like multiple conditions in a single predicate.

Performance Considerations

For in-memory collections, the main performance factor is the number of predicate evaluations. Multiple Where calls can be slightly less efficient than a single Where with && because each Where introduces an extra iterator layer. However, the overhead is small and usually negligible unless you are processing millions of elements.

The more significant factor is the order of filters. Placing the most restrictive condition first reduces the number of elements passed to subsequent filters. This is true whether you use multiple Where calls or a single Where with && and short-circuit evaluation. In a single Where with &&, the left operand is evaluated first, so you should put the cheaper or more selective condition on the left.

For IQueryable, the provider translates the entire expression into SQL, so the order of Where calls does not affect the number of rows fetched from the database. The database engine's query optimizer decides the execution plan. However, if you are using a provider that does not fully translate the expression (e.g., LINQ to Objects over a remote source), the behavior may differ.

When to Use Which Approach

Use multiple Where calls when you are building a query dynamically, for example when each condition is optional:

IQueryable<Person> query = dbContext.People; if (minAge.HasValue) query = query.Where(p => p.Age >= minAge.Value); if (!string.IsNullOrEmpty(namePrefix)) query = query.Where(p => p.Name.StartsWith(namePrefix));

This pattern is clean because each condition is added independently. With a single Where, you would need to build a dynamic expression tree, which is more complex.

Use a single Where with && when all conditions are known at compile time and the predicate is short. It is more readable and avoids the overhead of multiple iterator layers for in-memory collections.

There is no universal "best" choice. The decision depends on whether the conditions are static or dynamic, and whether you are working with IEnumerable or IQueryable.

c# linq multiple where conditions: Practical Usage and Code | RYUSLOG DEV