FullJoin in .NET 11 - potentially

3/6/2026
2 minute read

We have Join. We have LeftJoin. We have RightJoin. But somehow we still don't have a proper full outer join in LINQ. That might come soon!


Disclaimer: Soon as in, the api seems to be approved, but there is no open PR that would add the function to the .NET SDK itself nor to Entity Framework! See dotnet/runtime#124787 and the tuple-overload discussion in dotnet/runtime#120596. So the exact shape can still change until .NET 11 is finalized.


What is a full outer join?

A full outer join returns all elements from both sequences.

  • If both sides match, you get a pair.
  • If only the left side has an element, you still get that element.
  • If only the right side has an element, you also still get that element.
var customers = new[]
{
    new Customer(1, "Alice"),
    new Customer(2, "Bob"),
    new Customer(3, "Charlie")
};

var orders = new[]
{
    new Order(1, 100),
    new Order(1, 101),
    new Order(4, 102)
};

With a FullJoin, the result would conceptually contain:

  • Alice + Order 100
  • Alice + Order 101
  • Bob + no order
  • Charlie + no order
  • no customer + Order 102

That last entry is the important one. Join would drop it. LeftJoin would also drop it. FullJoin keeps it.

The proposed API

The main overload looks like this:

public static IEnumerable<TResult> FullJoin<TOuter, TInner, TKey, TResult>(
    this IEnumerable<TOuter> outer,
    IEnumerable<TInner> inner,
    Func<TOuter, TKey> outerKeySelector,
    Func<TInner, TKey> innerKeySelector,
    Func<TOuter?, TInner?, TResult> resultSelector);

There is also an overload with an IEqualityComparer<TKey>?, plus corresponding APIs for Queryable and AsyncEnumerable. One important detail is this part:

Func<TOuter?, TInner?, TResult> resultSelector

Both sides are nullable. That makes sense, because in a full join either side can be missing. That is different from LeftJoin or RightJoin, where only one side can be absent.

Here a FullJoin example:

var results = customers.FullJoin(
    orders,
    c => c.CustomerId,
    o => o.CustomerId,
    (customer, order) => new
    {
        CustomerName = customer?.Name ?? "(no customer)",
        OrderId = order?.Id ?? 0
    });

If the new API lands, I will update the LINQ Mindmap!

An error has occurred. This application may no longer respond until reloaded.Reload x