The New LeftJoin and RightJoin Methods in EF Core: Simplifying Joins in .NET 10
Bhrugen Patel
Author
Entity Framework Core - The Complete Guide
Learn the basic's of Entity Framework in ASP.NET Core / .NET 7 as we start from scratch and learn advance concepts.
LeftJoin and RightJoin in .NET 10: The Feature We've Been Waiting For
Okay, let's be real. If you've ever tried to do a left join in Entity Framework Core, you know the pain. That whole GroupJoin → SelectMany → DefaultIfEmpty dance? Yeah, nobody's writing that from memory without checking Stack Overflow first.
Well, good news! .NET 10 finally gave us what we've been asking for: actual LeftJoin and RightJoin methods. Let me show you why this is such a big deal.
The Old Way Was... Not Great
Remember this nightmare?
var result = await context.Students
.GroupJoin(
context.Departments,
student => student.DepartmentID,
department => department.ID,
(student, departments) => new { student, departments })
.SelectMany(
x => x.departments.DefaultIfEmpty(),
(x, department) => new
{
Name = x.student.FirstName,
Dept = department.Name ?? "No Department"
})
.ToListAsync();
Like... what even is that? Every time I had to write this, I'd just copy-paste from a previous project and hope for the best.
The New Way Is Beautiful
Here's the same thing with the new LeftJoin:
var result = await context.Students
.LeftJoin(
context.Departments,
student => student.DepartmentID,
department => department.ID,
(student, department) => new
{
Name = student.FirstName,
Dept = department.Name ?? "No Department"
})
.ToListAsync();
That's it. That's the whole thing. It actually reads like English!
A Real Example: Blog Posts and Authors
Let's use something we can all relate to. Imagine you're building a blog (meta, I know).
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; }
public int? AuthorId { get; set; }
}
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
}
Sample Data:
BlogPosts Table:
| Id | Title | AuthorId |
|---|---|---|
| 1 | Getting Started with .NET | 101 |
| 2 | Advanced C# Tips | 102 |
| 3 | Draft Post Ideas | null |
| 4 | EF Core Performance | 101 |
Authors Table:
| Id | Name |
|---|---|
| 101 | John Smith |
| 102 | Sarah Johnson |
| 103 | Mike Davis |
Now you want to list ALL posts, even if some don't have an author assigned yet (maybe they're drafts or the author left the company).
With LeftJoin:
var posts = await context.BlogPosts
.LeftJoin(
context.Authors,
post => post.AuthorId,
author => author.Id,
(post, author) => new
{
post.Title,
AuthorName = author.Name ?? "Anonymous"
})
.ToListAsync();
LeftJoin Result (All Posts):
| Title | AuthorName |
|---|---|
| Getting Started with .NET | John Smith |
| Advanced C# Tips | Sarah Johnson |
| Draft Post Ideas | Anonymous ← No author? No problem! |
| EF Core Performance | John Smith |
✅ What Happened?
LeftJoin kept ALL posts (left table) and matched them with authors where possible. The "Draft Post Ideas" didn't have an author, so we used "Anonymous" as the default value.
What About RightJoin?
RightJoin is like LeftJoin's mirror twin. It keeps everything from the RIGHT table instead.
var authorsAndPosts = await context.Authors
.RightJoin(
context.BlogPosts,
author => author.Id,
post => post.AuthorId,
(author, post) => new
{
PostTitle = post.Title,
AuthorName = author.Name ?? "No Author"
})
.ToListAsync();
RightJoin Result (All Posts, Matched with Authors):
| PostTitle | AuthorName |
|---|---|
| Getting Started with .NET | John Smith |
| Advanced C# Tips | Sarah Johnson |
| Draft Post Ideas | No Author |
| EF Core Performance | John Smith |
📝 Notice
Notice that Mike Davis (author 103) doesn't appear in the results because he has no blog posts. RightJoin kept all POSTS (right table) and matched authors where they exist.
But Wait, What About Navigation Properties?
Fair question! If you've set up your entities properly with navigation properties, you can often do this:
var posts = await context.BlogPosts
.Include(p => p.Author)
.Select(p => new
{
p.Title,
AuthorName = p.Author.Name ?? "Anonymous"
})
.ToListAsync();
So When Do I Use LeftJoin?
Good rule of thumb:
Use navigation properties when:
- • You have a proper relationship defined in your models
- • You're working with your main domain entities
- • You need to update or delete things
Use LeftJoin when:
- • You're doing reporting or analytics
- • You need to join tables that don't have a relationship
- • You're writing read-only queries
- • You want more control over the exact SQL being generated
💡 Think of it like this: navigation properties are for your app's main workflows, LeftJoin is for when you need to do something special.
The Bottom Line
These new methods are one of those "finally!" moments. They make code easier to write, easier to read, and easier to maintain. No more copying mysterious GroupJoin code from Stack Overflow.
⚡ Quick Reference:
- LeftJoin keeps everything from the left (first) table
- RightJoin keeps everything from the right (second) table
- Use
??to handle nulls when there's no match - Remember: Both tables participate, but one table's records are always included
That's really all there is to it. Happy coding!