LINQ
Full Outer Join
Data Query
C#
Programming

LINQ - Full Outer Join

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

LINQ does not provide a built-in FullOuterJoin operator the way SQL provides FULL OUTER JOIN. To get the same effect, you typically combine a left outer join with the rows from the right side that did not match anything on the left.

What a Full Outer Join Means

A full outer join returns:

  • all matching pairs,
  • all left-side rows with no match,
  • and all right-side rows with no match.

That makes it different from:

  • an inner join, which keeps only matches,
  • a left outer join, which keeps all left-side rows,
  • and a right outer join, which keeps all right-side rows.

In LINQ, you build the full join behavior from smaller pieces.

Sample Data

Suppose we have employees and departments:

csharp
1using System;
2using System.Collections.Generic;
3
4var employees = new[]
5{
6    new { Id = 1, Name = "Ada", DepartmentId = 10 },
7    new { Id = 2, Name = "Linus", DepartmentId = 20 },
8    new { Id = 3, Name = "Grace", DepartmentId = 99 },
9};
10
11var departments = new[]
12{
13    new { Id = 10, Title = "Engineering" },
14    new { Id = 20, Title = "Research" },
15    new { Id = 30, Title = "Finance" },
16};

Employee Grace has no matching department, and Finance has no matching employee. A full outer join should include both unmatched sides.

Build the Left Outer Join

In LINQ, a left outer join is usually written with join ... into plus DefaultIfEmpty():

csharp
1var leftJoin =
2    from e in employees
3    join d in departments on e.DepartmentId equals d.Id into groupJoin
4    from d in groupJoin.DefaultIfEmpty()
5    select new
6    {
7        EmployeeName = e.Name,
8        DepartmentTitle = d?.Title
9    };

This gives every employee, even when no department exists.

Add the Right-Only Rows

To complete the full outer join, add departments that were not matched by any employee:

csharp
1var rightOnly =
2    from d in departments
3    join e in employees on d.Id equals e.DepartmentId into groupJoin
4    from e in groupJoin.DefaultIfEmpty()
5    where e == null
6    select new
7    {
8        EmployeeName = (string?)null,
9        DepartmentTitle = d.Title
10    };

Now combine both sequences:

csharp
1var fullOuterJoin = leftJoin.Concat(rightOnly);
2
3foreach (var row in fullOuterJoin)
4{
5    Console.WriteLine($"{row.EmployeeName ?? "(no employee)"} - {row.DepartmentTitle ?? "(no department)"}");
6}

That is the core LINQ pattern for a full outer join.

Why This Works

The left outer join already includes:

  • all matches,
  • and all unmatched left-side rows.

So the only missing records are the unmatched right-side rows. The second query finds exactly those and appends them.

This is often simpler than trying to force one giant query expression to simulate the full join in a single block.

A Reusable Helper for LINQ to Objects

For LINQ to Objects, you can wrap the pattern in a helper method:

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5public static class JoinExtensions
6{
7    public static IEnumerable<TResult> FullOuterJoin<TLeft, TRight, TKey, TResult>(
8        this IEnumerable<TLeft> left,
9        IEnumerable<TRight> right,
10        Func<TLeft, TKey> leftKey,
11        Func<TRight, TKey> rightKey,
12        Func<TLeft?, TRight?, TResult> result)
13    {
14        var leftJoin = from l in left
15                       join r in right on leftKey(l) equals rightKey(r) into gj
16                       from r in gj.DefaultIfEmpty()
17                       select result(l, r);
18
19        var rightOnly = from r in right
20                        join l in left on rightKey(r) equals leftKey(l) into gj
21                        from l in gj.DefaultIfEmpty()
22                        where l == null
23                        select result(l, r);
24
25        return leftJoin.Concat(rightOnly);
26    }
27}

This is convenient for in-memory collections. For database-backed providers, translation support varies, so test the generated SQL carefully.

LINQ to Objects Versus LINQ Providers

This distinction matters. In LINQ to Objects, you are just composing C# enumerables, so the pattern is straightforward. In LINQ providers such as Entity Framework, not every advanced LINQ composition translates cleanly to SQL.

If the query becomes awkward or poorly translated, sometimes the simplest solution is:

  • write the SQL directly,
  • or materialize the data and do the full join in memory if the dataset is small enough.

Common Pitfalls

The biggest pitfall is assuming LINQ has a native FullOuterJoin operator. It does not.

Another mistake is concatenating a left outer join with a full right outer join instead of only the unmatched right-side rows. That produces duplicates for already matched pairs.

Developers also sometimes forget that DefaultIfEmpty() is the crucial part that turns a grouped join into an outer join.

Finally, if you are querying through an ORM, verify whether the provider can translate the query efficiently before assuming the LINQ shape is production-ready.

Summary

  • LINQ has no built-in FullOuterJoin.
  • A practical full outer join is a left outer join plus the unmatched rows from the right side.
  • 'join ... into with DefaultIfEmpty() is the basic outer-join building block.'
  • The pattern is easiest in LINQ to Objects and may need extra care with ORM providers.
  • Avoid duplicate rows by appending only right-side rows that had no left-side match.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.