EF Core 10 Just Got Cleaner LeftJoin & RightJoin in LINQ
EF Core 10 introduces first-class LeftJoin and RightJoin operators, making LINQ queries cleaner, more readable, and easier to maintain.
If you've worked with relational databases long enough, you already know how essential LEFT JOIN and RIGHT JOIN are.
But if you've used Entity Framework Core before .NET 10β¦ you also know the pain π
Writing a simple left join in LINQ used to feel unnecessarily complicated β involving GroupJoin, DefaultIfEmpty, and SelectMany. It worked, but the intent was far from obvious.
Good news: EF Core 10 finally introduces LeftJoin and RightJoin β making your code much closer to how you think.
π€ What is a LEFT JOIN (simple explanation)?
A LEFT JOIN returns:
- All records from the left side
- Matching records from the right side
- If no match β right side =
null
π Example scenario
Imagine you have:
CustomersOrders
π You want to show all customers, even those who haven't placed any orders yet.
π The Old Way (Before .NET 10)
To perform a left join, you had to write something like this:
Query syntax
var query =
from customer in dbContext.Customers
join order in dbContext.Orders
on customer.Id equals order.CustomerId into orderGroup
from subOrder in orderGroup.DefaultIfEmpty()
select new
{
CustomerId = customer.Id,
customer.Name,
OrderId = (int?)subOrder.Id ?? 0,
Total = (decimal?)subOrder.Total ?? 0
};
Method syntax
var query = dbContext.Customers
.GroupJoin(
dbContext.Orders,
customer => customer.Id,
order => order.CustomerId,
(customer, orders) => new { customer, orders })
.SelectMany(
x => x.orders.DefaultIfEmpty(),
(x, order) => new
{
CustomerId = x.customer.Id,
x.customer.Name,
OrderId = (int?)order!.Id ?? 0,
Total = (decimal?)order!.Total ?? 0
});
π΅ Why this is painful:
- Too verbose for such a common operation
- Hard to read (intent is hidden)
- Easy to get wrong
- Not beginner-friendly
π The New Way: LeftJoin (EF Core 10)
Now we can finally write what we mean:
var query = dbContext.Customers
.LeftJoin(
dbContext.Orders,
customer => customer.Id,
order => order.CustomerId,
(customer, order) => new
{
CustomerId = customer.Id,
customer.Name,
OrderId = (int?)order.Id ?? 0,
Total = (decimal?)order.Total ?? 0
});
π₯ Why this is a big deal
- β Much cleaner & shorter
- β Intent is obvious immediately
- β No more GroupJoin + DefaultIfEmpty
- β Same SQL generated under the hood
π What about RIGHT JOIN?
EF Core 10 also introduces RightJoin.
π It does the opposite:
- Keeps all records from the right side
- Matches from the left (if available)
π Example
Say you want:
π Show all orders, even if some customers were deleted or missing
var query = dbContext.Customers
.RightJoin(
dbContext.Orders,
customer => customer.Id,
order => order.CustomerId,
(customer, order) => new
{
CustomerName = customer.Name,
OrderId = order.Id,
order.Total
});
β οΈ Important Notes
- Currently, query syntax (from ... select) does NOT support LeftJoin/RightJoin
- You must use method syntax
βΈ»
π§ When will you use this?
Honestly⦠everywhere:
- Customers with optional orders
- Users with optional profiles
- Products with optional discounts
- Employees with optional departments
Before, people sometimes:
- Wrote multiple queries β
- Used INNER JOIN accidentally (losing data) β
Now β no excuse π
βΈ»
π οΈ Best Practices
- Always handle nulls on the optional side
order.Total ?? 0
- Keep your projections lean (avoid selecting unnecessary columns)
- Add indexes on join keys for performance
βΈ»
π§Ύ Final Thoughts
This is one of those small features that makes a huge difference in daily coding.
With LeftJoin and RightJoin:
- Code is cleaner
- Intent is clearer
- Bugs are less likely
π Finally, LINQ feels closer to SQL β without the pain.
Support My Work
If you've enjoyed my content and found it helpful, consider buying me a coffee. It keeps me caffeinated and creating!
Buy me a coffee