Three Ways to Load Related Data
When an entity has navigation properties, such as a Blog with a collection of Posts, EF Core will not populate them automatically unless you tell it to. There are three strategies: eager loading, which fetches related data as part of the initial query using Include(); lazy loading, which fetches related data automatically the first time a navigation property is accessed; and explicit loading, which fetches related data on demand via the Entry() API after the parent entity is already loaded. Each strategy trades off query count, memory usage, and control differently.
Cricket analogy: It is like three ways a captain gets fielding data: pre-match, the whole fielding plan is briefed at once (eager); during play, a fielder is repositioned the instant a specific batter comes in (lazy); or the captain calls a specific field change only when he decides to (explicit).
Eager Loading with Include and ThenInclude
Eager loading uses Include() to specify a navigation property to load as part of the original query, and ThenInclude() to chain into a further level of nested navigation, for example context.Blogs.Include(b => b.Posts).ThenInclude(p => p.Comments). EF Core generates a single SQL query using JOINs (or, since EF Core 5, split queries via AsSplitQuery() to avoid a cartesian explosion) so related data arrives in the same round trip as the parent entities, which is usually the most predictable and performant option for data you know you will need.
Cricket analogy: It is like a broadcaster pulling player stats, career average, current form, and head-to-head record, all in one combined graphic package before the toss, rather than fetching each stat separately as commentary progresses.
// Eager loading: one round trip fetches blogs, their posts, and each post's comments
var blogs = await context.Blogs
.Include(b => b.Posts)
.ThenInclude(p => p.Comments)
.AsSplitQuery() // avoids a JOIN cartesian explosion for multiple collections
.ToListAsync();Lazy Loading and the N+1 Trap
Lazy loading requires the Microsoft.EntityFrameworkCore.Proxies package, UseLazyLoadingProxies() in your DbContext configuration, and navigation properties marked virtual. Once enabled, simply accessing blog.Posts triggers a fresh database query at that moment, generating a runtime proxy that intercepts the property getter. The danger is the N+1 problem: iterating over 50 blogs and accessing .Posts inside the loop fires one query for the blogs plus 50 additional queries, one per blog, instead of a single JOIN-based query.
Cricket analogy: It is like a scorer who only looks up a batter's career strike rate the instant that batter walks in to bat, meaning across an 11-man innings you make 11 separate trips to the record book instead of pulling the whole squad's stats beforehand.
Lazy loading is convenient but dangerous inside loops. Iterating over a collection and accessing a navigation property per item generates one query per iteration. Prefer eager loading with Include() whenever you know upfront which related data a request will need.
Explicit Loading with the Entry API
Explicit loading gives you precise, on-demand control without proxies or virtual properties. Using context.Entry(blog).Collection(b => b.Posts).LoadAsync() for collections or context.Entry(post).Reference(p => p.Author).LoadAsync() for single references, you decide exactly when related data is fetched, often useful in conditional branches where you only need related data under specific circumstances, such as loading comments only if a post's status is 'Published'.
Cricket analogy: It is like a team analyst who pulls up a bowler's spell-by-spell breakdown only when the captain specifically asks for it mid-match, a deliberate, conditional lookup rather than an automatic or blanket one.
Explicit loading also supports filtered loading: context.Entry(blog).Collection(b => b.Posts).Query().Where(p => p.IsPublished).Load() lets you load only a subset of a related collection instead of the entire set.
- Eager loading (Include/ThenInclude) fetches related data in the same query as the parent, typically the safest default.
- Lazy loading fetches related data automatically the first time a navigation property is accessed, requiring the Proxies package and virtual properties.
- Lazy loading inside a loop causes the N+1 problem: one query per iteration instead of a single batched query.
- Explicit loading via context.Entry().Collection()/Reference().Load() gives on-demand, conditional control.
- AsSplitQuery() avoids cartesian-explosion row duplication when eagerly loading multiple collection navigations.
- Explicit loading supports filtered queries on the related collection via .Query().Where(...).
- Choosing the right strategy is a tradeoff between query count, payload size, and code simplicity.
Practice what you learned
1. Which loading strategy fetches related data as part of the original query using a single round trip?
2. What is required to enable lazy loading in EF Core?
3. What problem commonly occurs when lazy loading is used inside a loop over many parent entities?
4. Which API is used for explicit loading of a related collection?
Was this page helpful?
You May Also Like
LINQ to Entities
How EF Core translates LINQ query syntax into SQL, and the rules that govern what can and cannot be pushed down to the database.
Projections with Select
Using Select to shape query results into DTOs, reducing data transfer and avoiding unnecessary change tracking overhead.
Filtering, Sorting, and Paging
Building efficient, parameterized queries with Where, OrderBy, and Skip/Take, plus the tradeoffs of offset versus keyset pagination.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics