Back to Blog
C#

C# LINQ Null Handling: Avoiding NullReferenceException in Queries

Practical ways to handle null sources, null elements, and null properties in C# LINQ queries using null-conditional operators, DefaultIfEmpty, and safe projections.

LINQNullReferenceExceptionC# NullableNull-Conditional OperatorDefaultIfEmpty
Illustration of C# LINQ query with null safety mechanisms like null-conditional operator and DefaultIfEmpty.

Handling null values is one of the most common sources of runtime failures in C# LINQ queries. Whether the source collection is null, an element is null, or a property accessed inside a projection is null, the default behavior often throws NullReferenceException. This article covers practical C# LINQ techniques for handling these null cases without obscuring the query's intent.

Common Null Scenarios in LINQ Queries

LINQ queries fail with null references in a few predictable places. The source sequence itself can be null, as when a method returns IEnumerable<T> but returns null instead of an empty collection. Individual elements can be null, which breaks Where clauses that dereference element properties. Projections that access nested properties also throw when an intermediate object is null. Finally, aggregate operations like FirstOrDefault return null for reference types, and subsequent chained calls on that result can fail. Recognizing these patterns helps you decide which guard is appropriate.

Using Null-Conditional Operators in Projections

The null-conditional operator ?. is the first line of defense inside Select projections. It short-circuits property access when the receiver is null, returning null instead of throwing.

var customers = new List<Customer> { new Customer { Name = "Alice", Address = new Address { City = "Berlin" } }, new Customer { Name = "Bob", Address = null }, null }; var cityNames = customers .Select(c => c?.Address?.City) .ToList();

The ?. operator ensures that neither a null c nor a null c.Address raises an exception. The result list contains "Berlin", null, and null. This is useful when you need to preserve the shape of the output and later handle nulls explicitly. Note that ?. does not filter out null entries; it only prevents the exception. If you need to exclude them, combine it with a Where clause.

Handling Null Source Collections with ?. and ??

A null source collection is a different problem. Even Where will throw if the source is null. The null-conditional operator can be applied to the whole sequence, and ?? provides a fallback value.

IEnumerable<Order> orders = GetOrders(); // may return null var total = orders? .Where(o => o.IsActive) .Sum(o => o.Amount) ?? 0;

Here orders?. returns null if orders is null, so the entire chain short-circuits. The ?? 0 supplies a default value for the Sum result. This pattern is compact and avoids scattering null checks throughout the query. It works for any LINQ method that returns a single value. For methods that return a sequence, such as Where or Select, use ?? Enumerable.Empty<T>() to produce an empty sequence.

var activeOrders = orders? .Where(o => o.IsActive) .ToList() ?? new List<Order>();

Using DefaultIfEmpty for Empty and Null Sequences

DefaultIfEmpty gives you a fallback value when a sequence is empty. It does not, by itself, handle a null source; if the source is null, the whole chain is skipped before DefaultIfEmpty runs. Combine the source with ?? when you need to handle both cases.

var numbers = new List<int?> { 1, null, 3 }; var safeNumbers = (numbers ?? Enumerable.Empty<int?>()) .Where(n => n.HasValue) .Select(n => n.Value) .DefaultIfEmpty(0) .ToList();

If numbers is null, ?? replaces it with an empty sequence, and DefaultIfEmpty(0) supplies 0. If numbers is non-null but has no valid values, the same fallback applies. DefaultIfEmpty is most useful when you want to guarantee at least one element in the output, which is often required before calling First() or Single().

Null Handling in Joins and Grouping

Joins and grouping introduce additional null risks. The elements or key selectors involved may be null, and a left-join shape with DefaultIfEmpty() produces a null order for customers without matching orders. If orders itself can be null, convert it to an empty sequence before joining. Grouping by a key that can be null is allowed in LINQ to Objects, but any subsequent access to the grouping's elements must still check for null if the elements themselves can be null.

var query = from customer in customers where customer != null join order in orders on customer.Id equals order.CustomerId into customerOrders from order in customerOrders.DefaultIfEmpty() select new { CustomerName = customer.Name ?? "Unknown", OrderTotal = order?.Amount ?? 0 };

The where customer != null filter protects the join key, because customer.Id would throw if customer were null. The DefaultIfEmpty() inside the join ensures that customers without orders still appear, with order set to null. The null-conditional operator then safely accesses order.Amount. Without the DefaultIfEmpty() and order?.Amount guards, the query would throw when a customer has no matching orders.

Performance and Allocation Considerations

The null-conditional operator does not add significant runtime cost; it compiles to a simple null check before the member access. In most query pipelines, the dominant cost is the iteration and delegate invocation, not the null guard. However, using ?. inside a Select that is executed against an in-memory collection is straightforward. When the query is translated to SQL by an ORM like Entity Framework Core, the null-conditional operator may be translated to SQL CASE expressions, which can affect query complexity. In that scenario, it is often better to use the database's own null handling, such as COALESCE, through the ORM's mapping, rather than relying on client-side null guards. For in-memory LINQ, the allocation overhead of creating intermediate null values is negligible unless you are processing millions of elements and every projection allocates a new anonymous type. In such cases, consider reusing a single type or using a struct-based approach, but measure before optimizing.

Maintainability and Choosing the Right Approach

The choice between ?., ??, and DefaultIfEmpty depends on the intended behavior. Use ?. when you want to propagate nulls through the pipeline and handle them later. Use ?? when you need a concrete fallback value immediately. Use DefaultIfEmpty when you must guarantee at least one element in the sequence, especially before First() or Single(). Avoid mixing these operators in a way that hides meaningful null values. For example, if a null customer name is a data integrity issue, mapping it to "Unknown" might mask the problem. In that case, let the null propagate and handle it at the boundary. Consistency also matters: if you use ?. in one part of a query, use it throughout the same projection to avoid partial null protection. When writing reusable query helpers, prefer returning an empty collection instead of null from methods, as that removes the need for null-conditional guards on the source. These decisions affect long-term maintainability more than the syntax itself, so choose the approach that makes the null behavior explicit and testable.

How to Handle Null Values in C# LINQ Queries | RYUSLOG DEV