Back to Blog
C#

C# LINQ Single: Usage, Exceptions, and When to Use It

c# linq single: Understand C# LINQ Single: its behavior, exceptions, and when to use it over First or SingleOrDefault.

LINQC#SingleOrDefaultException HandlingQuery Operators
C# LINQ Single method illustration showing a sequence with exactly one matching element and an exception for multiple matches.

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

When you need to assert that a sequence contains exactly one matching element, LINQ's Single method is the tool. But it comes with strict runtime behavior that can surprise developers who expect it to behave like First or SingleOrDefault. This article explains what Single does, why it throws, and how to choose the right operator for your scenario.

What Does Single Do?

Single returns the only element of a sequence that satisfies a specified condition. If the sequence contains no matching element, or more than one matching element, it throws an exception. The method is defined in System.Linq.Enumerable and works with any IEnumerable<T>.

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

The parameterless overload expects the entire sequence to contain exactly one element. The predicate overload expects exactly one element to satisfy the condition.

The Two Exceptions You Must Handle

Single throws two different exceptions depending on the failure mode:

  • InvalidOperationException when the sequence is empty or no element matches the predicate.
  • InvalidOperationException when more than one element matches the predicate.

Both exceptions are the same type, which means you cannot distinguish between "no match" and "multiple matches" without inspecting the sequence yourself. This is a deliberate design choice: Single is meant to assert a unique match, and any deviation is a violation of the contract.

var numbers = new[] { 1, 2, 3 }; try { var single = numbers.Single(n => n > 2); // Throws because 3 is the only match? Actually 3 is the only match, so returns 3. } catch (InvalidOperationException) { // Handle the case where the sequence does not have exactly one match. }

In the example above, n > 2 matches only the value 3, so Single returns 3. If the predicate were n > 1, it would match 2 and 3, causing an exception.

When to Use Single Over First

First returns the first matching element and does not care about additional matches. Single enforces uniqueness. Use Single when the presence of more than one match indicates a data integrity problem. For example, querying a primary key from a database table should return exactly one row. If it returns two, something is wrong.

var user = dbContext.Users.Single(u => u.Id == userId);

If your data model guarantees uniqueness, Single provides a runtime check that catches bugs early. However, if the sequence is large and multiple matches are possible, Single will enumerate until it finds the second match, which can be inefficient. First stops at the first match.

Single vs SingleOrDefault: Which One Fits?

SingleOrDefault returns the default value (null for reference types, default for value types) when no match exists, but still throws if more than one match exists. This is useful when you expect zero or one match, but never more than one.

MethodNo matchOne matchMultiple matches
SingleThrowsReturns elementThrows
SingleOrDefaultReturns defaultReturns elementThrows
FirstThrowsReturns firstReturns first
FirstOrDefaultReturns defaultReturns firstReturns first

Choose Single when a missing element is an error. Choose SingleOrDefault when a missing element is a valid state. For example, looking up an optional profile where the user may not have one: SingleOrDefault returns null, which you can handle gracefully.

Performance and Enumeration Behavior

Single must verify that no second match exists. It does this by enumerating the sequence until it finds a second matching element. If the sequence has many elements and the match is early, Single will still continue until it finds a second match or reaches the end. This can be significantly slower than First, which stops at the first match.

Consider a list of a million items where you expect exactly one match. Single will scan the entire list unless it finds a second match earlier. First would stop at the first match, but it would not verify uniqueness. If you need uniqueness and performance matters, consider using a more targeted query, such as a dictionary lookup, or use Single only on collections that are already known to be small or indexed.

// Inefficient for large collections with early match var item = largeList.Single(x => x.Id == targetId); // More efficient if uniqueness is guaranteed by the data source var item = largeList.First(x => x.Id == targetId);

If you cannot guarantee uniqueness but want both speed and safety, you can write a custom check that stops after finding two matches:

var matches = largeList.Where(x => x.Id == targetId).Take(2).ToList(); if (matches.Count == 1) { /* use matches[0] */ } else { /* handle error */ }

This approach enumerates only until the second match is found, similar to Single, but gives you control over the error handling.

Common Pitfalls and Maintainability

One common mistake is using Single on a sequence that may legitimately have multiple matches, causing runtime exceptions in production. Another is catching InvalidOperationException and treating it as a generic error, which hides the distinction between "no match" and "multiple matches".

To improve maintainability, document the invariant that Single enforces. If you are using Single to assert uniqueness, add a comment or a custom exception message that explains what went wrong. For example:

try { var order = orders.Single(o => o.Id == orderId); } catch (InvalidOperationException ex) { throw new InvalidOperationException($"Expected exactly one order with ID {orderId}, but found a different count.", ex); }

This makes the failure mode clear to the next developer. Also, consider whether Single is the right operator for your data source. If you are querying a database via Entity Framework, Single translates to a SQL query that fetches the matching rows and checks the count. In some providers, it may fetch all matching rows, which can be inefficient. In that case, SingleOrDefault with a Take(2) might be more controlled.

Compatibility and Edge Cases

Single works with any IEnumerable<T>, including arrays, lists, and deferred queries. Be aware that deferred execution means the sequence is enumerated when Single is called, not when the query is defined. If the underlying data changes between query definition and enumeration, the result may differ.

For value types, SingleOrDefault returns default(T) when no match exists, which could be 0, false, or a struct with default values. This can be ambiguous if 0 is a valid value. In such cases, use Single and catch the exception, or use a nullable wrapper.

int? result = values.SingleOrDefault(v => v == target); if (result.HasValue) { /* use result.Value */ }

Single is not available in LINQ to Objects only; it also works with other LINQ providers like Entity Framework, but the translation and behavior may vary. Always test the generated query if you use Single with a database provider.

Choosing the Right Operator for Your Scenario

The decision between Single, SingleOrDefault, First, and FirstOrDefault depends on two questions: Is a missing match an error? Is more than one match an error?

  • Use Single when both are errors.
  • Use SingleOrDefault when missing is okay but multiple is not.
  • Use First when missing is an error but multiple is acceptable.
  • Use FirstOrDefault when both missing and multiple are acceptable.

For example, fetching a user by primary key: missing is an error, multiple is an error → Single. Fetching the most recent log entry: missing is okay, multiple is not relevant → FirstOrDefault with an order. Fetching a configuration value that may or may not exist but should be unique → SingleOrDefault.

When you need to enforce uniqueness, Single is the clearest expression of that intent. It makes the code self-documenting and catches data anomalies early. The cost is the extra enumeration and the exception handling, but in most business applications, the safety benefit outweighs the performance cost, especially when the data source enforces uniqueness at the database level.

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