---
title: "FullJoin in .NET 11 - potentially"
description: "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!"
updated: 2026-03-06
tags: [".NET", ".NET 11", "EF", "LINQ"]
canonical: "https://steven-giesel.com/blogPost/5a750fbf-cbf1-44dc-b864-70bedd8a29a9/fulljoin-in-net-11-potentially"
---

# FullJoin in .NET 11 - potentially

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](https://github.com/dotnet/runtime/issues/124787) and the tuple-overload discussion in [dotnet/runtime#120596](https://github.com/dotnet/runtime/issues/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.


```csharp
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:

```csharp
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:

```csharp
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:

```csharp
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](https://steven-giesel.com/blogPost/00443661-f75b-4694-852b-175b4a10d6c4/linq-mindmap-net-10-edition)!
