EFCore.FluentIncludes 1.2.0

dotnet add package EFCore.FluentIncludes --version 1.2.0
                    
NuGet\Install-Package EFCore.FluentIncludes -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EFCore.FluentIncludes" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EFCore.FluentIncludes" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="EFCore.FluentIncludes" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EFCore.FluentIncludes --version 1.2.0
                    
#r "nuget: EFCore.FluentIncludes, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EFCore.FluentIncludes@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EFCore.FluentIncludes&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=EFCore.FluentIncludes&version=1.2.0
                    
Install as a Cake Tool

EFCore.FluentIncludes

codecov

Simplify Entity Framework Core Include/ThenInclude chains with clean, readable path-based syntax.

Why?

EF Core's eager loading syntax becomes hard to read with nested navigation properties:

// Hard to read - what's actually being loaded?
var order = await context.Orders
    .Include(o => o.Customer)
        .ThenInclude(c => c.Address)
    .Include(o => o.Customer)
        .ThenInclude(c => c.PaymentMethods)
    .Include(o => o.LineItems)
        .ThenInclude(li => li.Product)
            .ThenInclude(p => p.Category)
    .FirstOrDefaultAsync(o => o.Id == id);

With EFCore.FluentIncludes, the same query becomes:

// Clear and scannable
var order = await context.Orders
    .IncludePaths(
        o => o.Customer.To().Address,
        o => o.Customer.To().PaymentMethods,
        o => o.LineItems.Each().Product.To().Category)
    .FirstOrDefaultAsync(o => o.Id == id);

Installation

dotnet add package EFCore.FluentIncludes

Requirements: EF Core 8.0+, .NET 8.0+

using EFCore.FluentIncludes;

Quick Reference

Method When to Use
.To() Navigate through a reference (single entity, especially if nullable)
.Each() Navigate through a collection (one-to-many)
.Where(predicate).Each() Filter a collection before including
.OrderBy(key).Each() Order a collection before including

Simple rule: Use To() for "has one", use Each() for "has many".

Usage

Basic Paths

// Single property
.IncludePaths(o => o.Customer)

// Chain with To() for references
.IncludePaths(o => o.Customer.To().Address)

// Chain with Each() for collections
.IncludePaths(o => o.LineItems.Each().Product)

// Combine both
.IncludePaths(o => o.LineItems.Each().Product.To().Category)

// Multiple paths at once
.IncludePaths(
    o => o.Customer.To().Address,
    o => o.LineItems.Each().Product.To().Category,
    o => o.Payments.Each().PaymentMethod)

Filtering and Ordering Collections

Filter and order collections during eager loading:

// Filter: only active items
.IncludePaths(o => o.LineItems.Where(li => li.IsActive).Each().Product)

// Order: by display order
.IncludePaths(o => o.LineItems.OrderBy(li => li.DisplayOrder).Each().Product)

// Combine: filter then order
.IncludePaths(o => o.LineItems
    .Where(li => li.IsActive)
    .OrderBy(li => li.DisplayOrder)
    .Each()
    .Product)

// Multiple sort keys
.IncludePaths(o => o.LineItems
    .OrderBy(li => li.Category)
    .ThenByDescending(li => li.DisplayOrder)
    .Each())

Alternative: The ! Operator

You can use ! instead of To() for nullable navigation:

.IncludePaths(o => o.Customer!.Address)  // Same as o => o.Customer.To().Address

Both are safe - the lambda is never executed. To() is more readable and consistent with Each().


Advanced Features

Conditional Includes

Include paths based on runtime conditions:

var order = await context.Orders
    .IncludePaths(o => o.Customer)
    .IncludePathsIf(includeProducts,
        o => o.LineItems.Each().Product.To().Category)
    .IncludePathsIf(includePayments,
        o => o.Payments.Each().PaymentMethod)
    .FirstOrDefaultAsync(o => o.Id == id);

Use cases: feature flags, user permissions, API query parameters.

Grouping Paths with IncludeFrom

When multiple paths share the same base, avoid repetition:

// Instead of repeating the filter:
.IncludePaths(
    o => o.LineItems.Where(li => li.IsActive).Each().Product,
    o => o.LineItems.Where(li => li.IsActive).Each().Discounts)

// Group them:
.IncludeFrom(
    o => o.LineItems.Where(li => li.IsActive).Each(),
    li => li.Product,
    li => li.Discounts.Each())

Also works with IncludeFromIf() for conditional grouped includes.

Reusable Specifications

Create reusable include patterns:

public class OrderDetailSpec : IncludeSpec<Order>
{
    public OrderDetailSpec()
    {
        Include(o => o.Customer.To().Address);
        Include(o => o.LineItems.Each().Product.To().Category);
    }
}

// Use it
var orders = await context.Orders
    .WithSpec<Order, OrderDetailSpec>()
    .ToListAsync();

Compose specs:

public class OrderFullSpec : IncludeSpec<Order>
{
    public OrderFullSpec()
    {
        IncludeFrom<OrderDetailSpec>();  // Include everything from another spec
        Include(o => o.Payments.Each());
    }
}

Multiple specs: .WithSpecs<Order, OrderDetailSpec, OrderAuditSpec>()

Conditional: .WithSpecIf<Order, OrderDetailSpec>(condition)


API Reference

Method Purpose
IncludePaths(paths...) Include one or more navigation paths
IncludePathsIf(condition, paths...) Include paths only when condition is true
IncludeFrom(basePath, subPaths...) Group multiple paths from a common base
IncludeFromIf(condition, basePath, subPaths...) Conditional grouped includes
Each() Navigate through a collection
Where(predicate).Each() Filter a collection
OrderBy(key).Each() Order a collection
To() Navigate through a reference property
WithSpec<TEntity, TSpec>() Apply a reusable specification
WithSpecs<TEntity, TSpec1, TSpec2>() Apply multiple specifications
WithSpecIf<TEntity, TSpec>(condition) Apply spec only when condition is true

Before & After

Scenario Standard EF Core EFCore.FluentIncludes
Nested property .Include(o => o.Customer).ThenInclude(c => c.Address) .IncludePaths(o => o.Customer.To().Address)
Through collection .Include(o => o.Items).ThenInclude(i => i.Product) .IncludePaths(o => o.Items.Each().Product)
Filtered collection .Include(o => o.Items.Where(i => i.Active))... .IncludePaths(o => o.Items.Where(i => i.Active).Each()...)
Deep nesting 4+ lines of Include/ThenInclude Single path expression

Performance

EFCore.FluentIncludes generates identical SQL to standard EF Core includes. Expression parsing adds microseconds of overhead - negligible compared to database query time.

On .NET 10+, source generation eliminates parsing overhead entirely using C# interceptors.

Works with AsSplitQuery() and AsSingleQuery().

Compile-Time Analysis

The included Roslyn analyzer catches common errors at compile time:

  • FI0001 - Property does not exist
  • FI0002 - Missing Each() on collection
  • FI0003 - Each() on non-collection
  • FI0007 - Nullable navigation without To() or !

Auto-fixes are available for common issues.

Sample Project

See samples/EFCore.FluentIncludes.Sample for a complete ASP.NET Core example.

dotnet run --project samples/EFCore.FluentIncludes.Sample

License

Apache 2.0 - See LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 97 1/27/2026
1.1.0 88 1/26/2026
1.0.0 92 1/25/2026
1.0.0-preview.5 49 1/24/2026
1.0.0-preview.4 45 1/23/2026
1.0.0-preview.3 49 1/22/2026
1.0.0-preview.2 49 1/22/2026
1.0.0-preview.1 49 1/22/2026