An Include call followed by one or more ThenInclude calls can often be written as a single Include call with
a dotted navigation path, when every navigation before the last step of the chain is a reference (single-valued) navigation.
Why is this an issue?
Each ThenInclude step can be folded into the Include call it chains onto once every step but the last is a reference
navigation. This is a purely syntactic simplification: Entity Framework Core generates the same SQL either way, and merging the chain never changes
what the query loads.
How to fix it
Replace the Include/ThenInclude chain with a single Include call whose lambda body is the equivalent dotted
navigation path.
Code examples
Noncompliant code example
var courses = await context.Courses
.Include(c => c.Department)
.ThenInclude(d => d.Administrator) // Noncompliant: "Department" and "Administrator" can be merged into one "Include"
.ToListAsync();
Compliant solution
var courses = await context.Courses
.Include(c => c.Department.Administrator)
.ToListAsync();
A collection navigation earlier in the chain doesn’t block merging the references that follow it:
var students = await context.Students
.Include(s => s.Enrollments)
.ThenInclude(e => e.Course) // Noncompliant: "Course" and "Department" can be merged into one "ThenInclude"
.ThenInclude(c => c.Department)
.ToListAsync();
var students = await context.Students
.Include(s => s.Enrollments)
.ThenInclude(e => e.Course.Department)
.ToListAsync();
Resources
Documentation
- Microsoft Learn - Loading related data - Eager loading, "Including multiple levels" section