DEV Community

Cover image for Expressing SQL Outer Joins in LINQ — Implementing LeftJoin, RightJoin and Shuffle
s-iguchi09
s-iguchi09

Posted on Edited on Originally published at s-iguchi09.github.io AI-assisted

Expressing SQL Outer Joins in LINQ — Implementing LeftJoin, RightJoin and Shuffle

.NET 10 adds LeftJoin, RightJoin, and Shuffle to LINQ. On .NET Framework 4.8 you still write a left join as GroupJoin + SelectMany + DefaultIfEmpty, and shuffle with OrderBy(_ => Guid.NewGuid()). Both are easy to get subtly wrong.

This is a short summary. The full article, with the complete polyfill and a net48 vs net10.0 output comparison, is here:
Expressing SQL Outer Joins in LINQ — Implementing LeftJoin, RightJoin and Shuffle

The core of the polyfill

LeftJoin wraps the classic idiom behind the .NET 10 signature, with TInner? marking the side that can be missing:

return outer
    .GroupJoin(inner, outerKeySelector, innerKeySelector, (o, inners) => new { o, inners }, comparer)
    .SelectMany(g => g.inners.DefaultIfEmpty(), (g, i) => resultSelector(g.o, i));
Enter fullscreen mode Exit fullscreen mode

RightJoin pivots the same GroupJoin onto the inner sequence. The whole file sits behind #if !NET10_0_OR_GREATER, so it switches off after migration.

Three things worth knowing

  1. Shuffle is a one-pass Fisher–Yates, not a GUID sort. On .NET Framework, eight threads that each created new Random() at the same moment produced the same order in the measurement. The fix seeds each thread from one locked instance.
  2. It re-shuffles on every enumeration. Call ToArray once if you need a fixed order.
  3. Do not use it on IQueryable<T>. The Enumerable method still binds, so an Entity Framework join silently runs in memory. Keep the GroupJoin form for database queries.

A Japanese version is also available.

Related Articles

Top comments (0)