In this lesson, you will learn how to shift from reliance on implicit navigation loading to explicit LINQ joins to gain precise control over your generated SQL.
The Problem with Implicit Navigation
When you use navigation properties (like order.Customer), EF Core relies on its internal model to decide how to fetch related data. While convenient, this often results in LEFT JOIN operations that include columns you don't need, or worse, Cartesian explosions when loading multiple collections. If you only need a specific subset of data from a related table, EF Core’s default Include behavior can be overly aggressive.
Taking Control with Explicit Joins
By using the LINQ Join method, you move away from the "black box" of navigation properties and force EF Core to construct a specific INNER JOIN. This is particularly useful when you want to filter or project data based on secondary tables without bringing the entire entity graph into memory.
Consider this example where we want to find orders placed by customers in a specific city:
var query = context.Orders
.Join(context.Customers,
order => order.CustomerId,
customer => customer.Id,
(order, customer) => new { order, customer })
.Where(x => x.customer.City == "Seattle")
.Select(x => new { x.order.Id, x.customer.Name });
Why This Optimizes Performance
- Precise SQL Generation: Instead of relying on EF Core to resolve the relationship through internal metadata, you explicitly define the join condition. This often leads to more compact, readable SQL that the database engine can optimize more effectively.
- Reduced Data Volume: By performing an explicit join, you are primed to use projections (the
selectstatement) immediately. This prevents the "Select *" behavior that often accompaniesInclude, significantly reducing the amount of data transferred from the database to your application. - Inner Join Default: Unlike
Include, which typically defaults toLEFT JOINto ensure no data is omitted, an explicitJoindefaults to anINNER JOIN. If you know the related data must exist for your business logic, anINNER JOINis almost always more performant at the database level.
Key Takeaway: Use explicit LINQ joins to replace broad navigation loading when you need to target specific related columns or enforce an INNER JOIN for better query performance.