S9129 — "Include" and "ThenInclude" chains of reference navigations should be merged into a single "Include" call

Language
C#
Type
Code smell
Severity
Minor
Tags
entity-framework-core, entity-framework, orm, database

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

Related rules

↑ Back to top