S9022 — Redundant "Include" calls should be removed

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

An Include or ThenInclude call can be entirely inert: Entity Framework Core generates the exact same SQL with or without it.

Why is this an issue?

Include and ThenInclude tell Entity Framework Core to eagerly load a related navigation together with the entity that owns it. In each of the following cases, that instruction has no effect on the generated SQL — it adds nothing but noise, and can safely be removed:

In every case, it can also mislead a reader into thinking it changes what is loaded.

Exceptions

The rule does not apply when:

How to fix it

Remove the Include (or ThenInclude) call that has no effect.

Code examples

Noncompliant code example


var students = await context.Students
    .Include(s => s.Enrollments) // Noncompliant: "Enrollments" is already included below
    .Include(s => s.Enrollments)
    .ToListAsync();

Compliant solution


var students = await context.Students
    .Include(s => s.Enrollments)
    .ToListAsync();

This repeated Include is not redundant — it branches into two different ThenInclude calls:


var students = await context.Students
    .Include(s => s.Enrollments).ThenInclude(e => e.Course)
    .Include(s => s.Enrollments).ThenInclude(e => e.Student) // Compliant: re-anchors before a different ThenInclude
    .ToListAsync();

An Include is redundant the same way when a projection further down the query already returns the navigation directly:


var students = await context.Students
    .Include(s => s.Enrollments) // Noncompliant: "Enrollments" is already required by the projection below
    .Select(s => new { s.LastName, s.Enrollments })
    .ToListAsync();

var students = await context.Students
    .Select(s => new { s.LastName, s.Enrollments })
    .ToListAsync();

A projection that only dereferences into the navigation — instead of returning it, as in the example above — makes the Include redundant just the same, because Entity Framework Core still needs the same join to compute that value:


var principals = await context.Students
    .Include(s => s.School) // Noncompliant: "School" is already required by the projection below
    .Select(s => s.School.Principal)
    .ToListAsync();

var principals = await context.Students
    .Select(s => s.School.Principal)
    .ToListAsync();

It is also redundant when the query never returns the entity at all:


var studentCount = await context.Students
    .Include(s => s.Enrollments) // Noncompliant: this "Include" has no effect; the following call to "CountAsync" never returns the entity
    .CountAsync();

var studentCount = await context.Students
    .CountAsync();

An Include is also redundant when it targets an owned entity — Entity Framework Core loads it together with its owner regardless:


var students = await context.Students
    .Include(s => s.HomeAddress) // Noncompliant: "HomeAddress" is an owned entity and is always loaded with its owner
    .ToListAsync();

var students = await context.Students
    .ToListAsync();

Resources

Documentation

Related rules

↑ Back to top