November 18 2025
This issue is made possible thanks to ZZZ Projects, who help keep this newsletter free for everyone. A huge shout-out to them for their support of our community. EF Core too slow? Insert data 14x faster and cut saving time by 94%. đ Boost performance with Bulk Insert
Every .NET dev whoâs touched a relational database knows LEFT JOIN and RIGHT JOIN. But in LINQ, writing a proper left join has always felt⌠heavier than it should.
Instead of something obvious like LeftJoin, weâve been chaining GroupJoin, SelectMany, and DefaultIfEmpty in exactly the right order, hoping the next developer remembers the pattern and doesnât âoptimizeâ it into an inner join. With .NET 10 (LTS, supported until November 2028) and EF Core 10, we finally get first-class LeftJoin and RightJoin operators in LINQ.
These operators read like SQL, translate to proper LEFT JOIN / RIGHT JOIN, and remove a ton of boilerplate.
In this article:
⢠Why were LeftJoin and RightJoin added ⢠How they map to EF Core queries and SQL ⢠Real-world examples youâll actually use in production ⢠Subtle details & limitations you should know before migrating
Letâs remind ourselves what EF Core used to require for a left join:
var query = from a in A join b in B on a.Id equals b.AId into g from b in g.DefaultIfEmpty() select new { a, b };
Or the even uglier method-syntax variant. This syntax works, but: ⢠Itâs noisy ⢠Itâs easy to break ⢠juniors constantly forget DefaultIfEmpty() ⢠It doesn't resemble SQL at all
You now have:
LeftJoin()RightJoin()
Supported by LINQ-to-Entities, meaning EF Core properly translates them to: ⢠LEFT JOIN ⢠RIGHT JOIN âŚwith clean, readable, intention-revealing syntax.
Orders + Latest Shipment Tracking
âShow all orders and the date of their most recent shipment, even if the order hasnât been shipped yet.â
This is a real scenario from e-commerce, logistics, and supply-chain systems.
Entities:
public class Order{ public Guid Id { get; set; } public string Customer { get; set; } = default!; public DateTime Created { get; set; }}Â public class Shipment{ public Guid Id { get; set; } public Guid OrderId { get; set; } public DateTime Sent { get; set; }}
EF Core 10 LeftJoin Query We first compute a âlatest shipment per orderâ:
var latestShipments = db.Shipments .GroupBy(s => s.OrderId) .Select(g => new { OrderId = g.Key, LastShipment = g.Max(x => x.Sent) });
Then we join orders with (optional) shipment info:
var query = db.Orders .LeftJoin( latestShipments, order => order.Id, shipment => shipment.OrderId, (order, shipment) => new { order.Id, order.Customer, order.Created, LastShipment = shipment?.LastShipment } ) .OrderByDescending(x => x.Created);
Why this example matters ⢠Itâs a real-world ecommerce/logistics use case. ⢠It shows a typical âlatest entry per groupâ pattern. ⢠It leverages grouping + left join in a clean, readable way
Employee Directory + Optional Workstation Assignment âShow all workstations and who is assigned to them, including empty workstations.â This is extremely common in:
⢠office management ⢠manufacturing floors ⢠call centers ⢠corporate IT asset management
Entities:
public class Workstation{ public int Id { get; set; } public string Location { get; set; } = default!;}Â public class Employee{ public Guid Id { get; set; } public string Name { get; set; } = default!; public int? StationId { get; set; }}
EF Core 10 RightJoin Query
var query = db.Employees .RightJoin( db.Workstations, emp => emp.StationId, ws => ws.Id, (emp, ws) => new { WorkstationId = ws.Id, ws.Location, EmployeeName = emp?.Name } ) .OrderBy(x => x.WorkstationId);
Why this example matters
⢠RightJoin makes sense here because the right table (workstations) is the primary dataset. ⢠It reflects real organizational data flows. ⢠It shows how to handle optional assignments cleanly.
If youâre hoping for a new C# keyword like:
from p in Productsleft join r in Reviews on p.Id equals r.ProductIdselect ...
âŚthatâs not what shipped in .NET 10.
As of .NET 10 / C# 14, LeftJoin and RightJoin are extension methods only on IQueryable
This is the âhappy pathâ for EF Core 10 and the one the team clearly optimized for:
var query = db.Orders .LeftJoin( db.Shipments, order => order.Id, shipment => shipment.OrderId, (order, shipment) => new { order, shipment } );
This gives you a clear intention and first-class translation to SQL LEFT JOIN / RIGHT JOIN.
You can technically wrap a method-based join inside a query expression:
var query = from x in db.Orders.LeftJoin( db.Shipments, o => o.Id, s => s.OrderId, (o, s) => new { o, s }) where x.o.Created >= cutoff select new { x.o.Id, x.s?.Sent };
But this is really just query syntax âaroundâ the method chain. Youâre not getting new query operators - youâre still using the LeftJoin extension method under the hood.
In practice, most teams that adopt LeftJoin / RightJoin will:
⢠Keep query syntax for simple from / where / select queries. ⢠Use method syntax (with the new join operators) whenever an outer join is involved.
Thatâs also the most readable compromise in code reviews.
LeftJoin and RightJoin look simple, but there are a few things worth keeping in mind when you start using them in real EF Core 10 codebases.
By definition: ⢠LeftJoin â the right side is nullable. ⢠RightJoin â the left side is nullable. Make that explicit in your projections and avoid accidental NullReferenceExceptions:
var result = db.Orders .LeftJoin( latestShipments, o => o.Id, ls => ls.OrderId, (o, ls) => new { o.Id, LastShipment = ls?.LastShipment, ShipmentLabel = ls != null ? $"Shipped at {ls.LastShipment:g}" : "Not shipped yet" } );
This pattern makes intent obvious and avoids the temptation to treat the joined entity as non-null.
Just because you can return entire entities on both sides doesnât mean you should.
Prefer projecting exactly what you need: ⢠Smaller SQL result sets ⢠Less data materialization ⢠Faster queries and less memory pressure
select new OrderSummaryDto{ Id = o.Id, Customer = o.Customer, LastShipment = ls?.LastShipment};
This is especially important when you join large tables (orders, events, logs, telemetry, etc.).
LeftJoin/RightJoin donât magically optimize the database - they still compile to regular SQL joins. The usual relational rule still applies: Join keys must be indexed if you care about performance. Typical examples: ⢠FK columns: Shipments.OrderId, Reviews.ProductId, JobExecution.JobId ⢠Business keys used in joins: ClientId, ExternalId, BankReference Without indexes, large outer joins will happily turn into table scans.
.NET 10 and EF Core 10 are a big release - complex types, improved JSON support, named query filters, better ExecuteUpdate, and more.
But for everyday data access, LeftJoin and RightJoin quietly fix one of the most annoying gaps in LINQ:
⢠No more GroupJoin + SelectMany + DefaultIfEmpty rituals ⢠Queries that look like LEFT JOIN / RIGHT JOIN ⢠Cleaner, more maintainable EF Core code that you can safely hand to juniors If you have codebases full of hand-rolled left joins:
⢠Start by refactoring the ugliest ones into LeftJoin ⢠Wire them into real features - dashboards, reports, export pipelines ⢠Use this as a teaching moment for your team: âThis is how we express joins in EF Core 10 from now on.â
And because this is an LTS release, you can confidently adopt these operators in production and enjoy them for years.
That's all from me for today.
Stop arguing about code style. In this course you get a production-proven setup with analyzers, CI quality gates, and architecture tests â the exact system I use in real projects. Join here.
Not sure yet? Grab the free Starter Kit â a drop-in setup with the essentials from Module 01.
Design Patterns that Deliver â Solve real problems with 5 battle-tested patterns (Builder, Decorator, Strategy, Adapter, Mediator) using practical, real-world examples. Trusted by 650+ developers.
Just getting started? Design Patterns Simplified covers 10 essential patterns in a beginner-friendly, 30-page guide for just $9.95.
Every Monday morning, I share 1 actionable tip on C#, .NET & Architecture that you can use right away. Join here.
Join 20,000+ subscribers who mass-improve their .NET skills with actionable tips on C#, Architecture & Best Practices.
Subscribe to the TheCodeMan.net and be among the 20,000+ subscribers gaining practical tips and resources to enhance your .NET expertise.