LINQ gets FullJoin in .NET 11 for full outer joins
The new FullJoin method in .NET 11 returns matched pairs plus unmatched rows from both sides as (Outer, Inner) tuples. Join, LeftJoin, RightJoin and GroupJoin also gain overloads without a result selector. The feature is in .NET 11 RC2, so details may still change before the final release.
Key points
- FullJoin returns every matched pair plus unmatched rows from both sides as (Outer, Inner) tuples.
- Join, LeftJoin and RightJoin gain overloads without a result selector that return tuples.
- GroupJoin's selector-free overload returns IGrouping<TOuter, TInner>, so the outer element is g.Key.
- With value-type elements a missing side becomes default(T); use nullable types to tell misses from real zeros.
- Unmatched right-side rows are appended after the left rows, so sort the result if you need a fixed order.
- FullJoin needs the .NET 11 SDK; on the .NET 10 SDK the call fails with CS1061.
Full outer joins have been missing from LINQ, so developers usually hand-write them with DefaultIfEmpty and a second query for the right-only rows. .NET 11 adds FullJoin to fill that gap. It takes two sequences, key selectors and an optional equality comparer, and returns every matched pair plus unmatched elements from either side. Missing sides become default values, which is null for reference types.
The release also adds selector-free overloads of Join, LeftJoin and RightJoin that return (Outer, Inner) tuples, while GroupJoin returns IGrouping instead. Watch a few edge cases: value types show a missing side as zero, null keys never match, and duplicate keys multiply. The API covers Enumerable, Queryable and AsyncEnumerable, but it needs the .NET 11 SDK, which is still a release candidate.
Real-world use
- Reconciling an inventory system against a physical stocktake to list matches, shortfalls and surplus items in one query
- Comparing a CRM export with a billing list to find records that exist on only one side
- Joining two IQueryable sources through a provider that supports the new operator, so the work runs on the database side
Why it matters
Reconciliation code that needed two queries and a DefaultIfEmpty can become one readable call with one shared key comparer. Test it on the .NET 11 SDK before the final release.