Back to Blog
C#

How to Use Where in C# LINQ

c# linq where: Learn how the Where method filters sequences in C# LINQ, covering syntax, predicate behavior, deferred execution, and practical performance considerations.

LINQC#FilteringQuery SyntaxIEnumerable
A visual representation of a C# LINQ Where filter selecting items from a collection, with a funnel metaphor.

The Where method is the core filtering operator in C# LINQ. It takes a sequence, applies a predicate to each element, and returns a new sequence containing only the elements that satisfy the condition. Understanding c# linq where is essential for writing readable, maintainable data manipulation code in C#.

How the Where Method Filters a Sequence

Where is an extension method defined in System.Linq on IEnumerable<T>. It accepts a Func<T, bool> predicate and returns an IEnumerable<T>. The predicate is invoked once per element, and only elements for which the predicate returns true are included in the result.

List<int> numbers = new() { ##### 1, 2, 3, 4, 5, }; IEnumerable<int> evenNumbers = numbers.Where(n => n % 2 == 0);

In this example, the lambda n => n % 2 == 0 is the predicate. It returns true for even numbers, so evenNumbers contains 2 and 4. The original list remains unchanged; Where does not modify the source sequence.

Method Syntax and Query Syntax for Where

C# LINQ supports two syntactic styles: method syntax and query syntax. Both compile to the same underlying calls, but they differ in readability.

Method syntax chains extension methods directly:

var adults = people.Where(p => p.Age >= ##### 18);

Query syntax uses a SQL-like expression that the compiler translates into method calls:

var adults = from p in people where p.Age >= 18 select p;

When you write from p in people where p.Age >= 18 select p, the compiler translates it to people.Where(p => p.Age >= 18). The where clause in query syntax maps directly to the Where method. Both forms are functionally identical, so the choice is mostly stylistic. Many developers prefer method syntax for simple filters and query syntax when multiple clauses (e.g., join, orderby) are involved.

Writing a Predicate: Parameters, Return Type, and Common Patterns

The predicate for Where must be a delegate that takes one element of the source type and returns a bool. It can be a lambda, a local function, or a named method.

static bool IsExpensive(Product product) => product.Price > 100; var expensiveProducts = products.Where(IsExpensive);

Lambdas are the most common because they allow inline logic. You can also use a predicate that captures variables from the enclosing scope:

decimal minPrice = 50; var filtered = products.Where(p => p.Price >= minPrice);

Be careful with captured variables: if the variable changes after the query is created, the predicate will see the updated value when the query is executed (due to deferred execution, discussed below).

A predicate can also include multiple conditions using logical operators:

var inStockAndCheap = products.Where(p => p.InStock && p.Price < 20);

This is equivalent to nesting two Where calls, but a single predicate is usually clearer and avoids iterating the sequence twice.

Deferred Execution and When the Filter Actually Runs

One of the most important behaviors of Where is deferred execution. The query is not executed at the point where you define it; it is executed when you enumerate the result. This means the predicate runs each time you iterate over the result.

var query = numbers.Where(n => n > 2); // No filtering has happened yet. foreach (var n in query) { Console.WriteLine(n); // The predicate runs here. }

If you enumerate the same query twice, the predicate runs twice. This has implications for performance and for side effects. If your predicate has side effects (e.g., logging or modifying a shared variable), they will occur on every enumeration. Avoid side effects in predicates.

Deferred execution also means that if the source sequence changes after the query is created, the query reflects the new state. For example, if you add an element to the list before enumerating, that element will be considered.

Combining Where with Other LINQ Operators

Where is often used in a chain with other operators such as Select, OrderBy, and GroupBy. The order of operations matters both for correctness and for performance.

var names = people .Where(p => p.Age >= 18) .OrderBy(p => p.LastName) .Select(p => p.FullName);

In this chain, Where runs first, reducing the number of elements that OrderBy and Select process. This is usually more efficient than filtering after a costly transformation. However, there are cases where you need to filter on a computed value, and then you might use Select before Where or use a predicate that computes the value internally.

var expensiveOrders = orders .Where(o => o.Items.Sum(i => i.Price) > 100);

Here the predicate computes a sum for each order. If you only need the total price later, you might want to project it first to avoid recomputation:

var orderTotals = orders .Select(o => new { Order = o, Total = o.Items.Sum(i => i.Price) }) .Where(x => x.Total > 100);

This projects the total once and then filters, which can be more efficient if the total is used again in the pipeline.

Performance and Allocation Considerations

Where itself is a streaming operator. It does not buffer the entire sequence; it yields each matching element as the source is enumerated. This makes it memory-efficient for large sequences.

When you chain multiple Where calls, each call adds a layer of iteration. For example, source.Where(p1).Where(p2) will iterate the source once and apply p1, then iterate the intermediate result and apply p2. The total work is the sum of the work for each predicate, but the source is only enumerated once. This is different from materializing intermediate results with ToList() or ToArray(), which allocates a new collection and forces full execution.

In general, prefer to combine conditions in a single predicate when they are logically related, but separate Where calls are fine when the conditions are independent and you want to reuse the intermediate filtered sequence.

For LINQ to Objects, Where is implemented as an iterator method, so it has minimal overhead per element. For LINQ to SQL or Entity Framework, the Where method is translated into a SQL WHERE clause, and the predicate is not executed in C# at all. This means that the behavior and performance characteristics are entirely different. You cannot use arbitrary C# methods in a LINQ-to-Entities query because they cannot be translated to SQL. Only expressions that the query provider can map to SQL are allowed.

Common Mistakes and How to Avoid Them

One common mistake is assuming that Where modifies the original collection. It does not; it returns a new sequence. If you need the filtered results as a list or array, call ToList() or ToArray().

var filteredList = products.Where(p => p.InStock).ToList();

Another mistake is using Where when you only need the first matching element. In that case, FirstOrDefault or SingleOrDefault is more efficient because it stops after finding a match, whereas Where would continue iterating the entire sequence if you then call First() on it.

// Inefficient: iterates the whole list even after finding a match. var firstExpensive = products.Where(p => p.Price > 100).First(); // Better: stops at the first match. var firstExpensive = products.First(p => p.Price > 100);

Also be aware that Where preserves the order of the source sequence. The output has the same relative order as the input. This is not guaranteed for all LINQ operators (e.g., OrderBy changes order), but Where is stable.

Finally, remember that the predicate must be a pure function in most cases. If you rely on side effects or on mutable state that changes during enumeration, you can get inconsistent results, especially with deferred execution.

Where with Different Data Sources

Where works with any IEnumerable<T>, including arrays, lists, dictionaries, and custom collections. When working with a Dictionary<TKey, TValue>, you can filter on the key or the value:

Dictionary<string, int> scores = new() { ["Alice"] = 90, ["Bob"] = 70, ["Charlie"] = 85 }; var highScorers = scores.Where(kv => kv.Value >= 80);

This returns a sequence of KeyValuePair<string, int> elements that match the condition. If you need a dictionary back, call ToDictionary().

For IQueryable<T> sources (e.g., Entity Framework), Where is part of the expression tree and is translated to the underlying query language. This allows filtering to happen in the database, reducing the amount of data transferred to the client. The same syntax is used, but the behavior is subject to the provider's translation rules.

Understanding how Where behaves in different contexts helps you write efficient and correct filtering logic, whether you are working with in in-memory collections or database queries.

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