EFCore.ComplexIndexes
5.3.0
dotnet add package EFCore.ComplexIndexes --version 5.3.0
NuGet\Install-Package EFCore.ComplexIndexes -Version 5.3.0
<PackageReference Include="EFCore.ComplexIndexes" Version="5.3.0" />
<PackageVersion Include="EFCore.ComplexIndexes" Version="5.3.0" />
<PackageReference Include="EFCore.ComplexIndexes" />
paket add EFCore.ComplexIndexes --version 5.3.0
#r "nuget: EFCore.ComplexIndexes, 5.3.0"
#:package EFCore.ComplexIndexes@5.3.0
#addin nuget:?package=EFCore.ComplexIndexes&version=5.3.0
#tool nuget:?package=EFCore.ComplexIndexes&version=5.3.0
EFCore.ComplexIndexes
Index support for complex type properties in EF Core migrations — the missing piece for value object-driven architectures.
EF Core models complex properties (value objects) but its migration tooling does not generate
indexes for their nested columns. This package hooks into EF Core's design-time pipeline and emits
the CREATE INDEX / DROP INDEX operations for you.
This is the core, provider-agnostic package. It works with any EF Core relational provider. For provider-specific index features, add a satellite package — each one includes this package automatically:
| Package | Adds |
|---|---|
| EFCore.ComplexIndexes.PostgreSQL | GIN/GiST/BRIN/SP-GiST/Hash methods, operator classes, INCLUDE, NULLS FIRST/LAST, expression & JSON indexes, temporal and exclusion constraints |
| EFCore.ComplexIndexes.SqlServer | Clustered, covering (INCLUDE), online builds, fill factor, sort-in-tempdb, data compression |
Setup
None. The migration differ is registered through EF Core's design-time tooling automatically —
install the package, declare your indexes in OnModelCreating, and run dotnet ef migrations add.
Usage
Single-column index on a complex property
builder.ComplexProperty(x => x.EmailAddress, c =>
c.Property(x => x.Value)
.HasComplexIndex(isUnique: true, filter: "deleted_at IS NULL")
);
Column names are resolved against the real model, so both convention-based names (Origin_Source)
and explicit HasColumnName overrides are honored.
A filter may name properties instead of columns — filter: "{DeletedAt} IS NULL" resolves to the
mapped column at migrations add, quoted for the provider, and is baked into the migration. A
selector reaches into complex properties and, for a value object mapped through a converter, one
step further: x => x.Email.Value resolves to the converted column.
Several indexes over one column
A property-level declaration holds one index per property. To give a column several differently-filtered indexes (the classic soft-delete pattern), declare them at the entity level — the selector reaches into complex properties, and each index needs its own explicit name:
builder.HasComplexIndex(x => x.EmailAddress.Value,
isUnique: true, filter: "deleted_at IS NULL", indexName: "ux_person_email_active");
builder.HasComplexIndex(x => x.EmailAddress.Value,
indexName: "ix_person_email_all");
Index names must be unique per table and within the provider's identifier limit (63 bytes on
PostgreSQL, which would otherwise truncate silently), and the package enforces both at
dotnet ef migrations add rather than letting the database reject — or quietly shorten — the name.
Composite index across scalar and nested properties
builder.HasComplexCompositeIndex(
x => new { x.Name, x.EmailAddress.Value },
isUnique: true);
Per-column sort direction
builder.HasComplexCompositeIndex(
c => new { c.Created, Counter = DbOrder.Desc(c.Version.Counter) },
indexName: "ix_commits_created_counter");
// CREATE INDEX ix_commits_created_counter ON ... (created, counter DESC);
Direction maps onto EF Core's native CreateIndexOperation.IsDescending, so every relational
provider renders it. Because a wrapped member is a method call, C# requires you to name it in the
anonymous type.
DbOrder.NullsFirst/NullsLast are declared here too, but rendering them is provider-specific —
see the PostgreSQL package.
Reading declarations back
GetComplexIndexes() on an entity type (or the model) returns every declaration — property-level,
entity-level, composite, expression — with its parts, IsUnique, Filter and explicit Name, so an
application can check its own conventions; FindComplexIndex(name) looks one up by explicit name.
It works on the mutable model inside OnModelCreating too, and the differ reads declarations
through the same code.
var unfiltered = modelBuilder.Model.GetEntityTypes()
.SelectMany(e => e.GetComplexIndexes())
.Where(ix => ix.IsUnique && ix.Filter is null);
Or install the convention: on the mutable model, AddComplexIndexFilter(predicate, where) ANDs a
predicate onto every selected declaration's filter, idempotently, and AddComplexIndex(definition)
adds one with the fluent API's identity rules. Both amend what is declared at the time of the call,
so run them after the configurations.
Documentation
Full documentation, including every provider-specific feature: https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes
Changelog
This package's changelog, or the root changelog covering all three packages.
MIT licensed.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Microsoft.EntityFrameworkCore.Abstractions (>= 10.0.0 && < 11.0.0)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on EFCore.ComplexIndexes:
| Package | Downloads |
|---|---|
|
SIL.Harmony
CRDT application library for building offline-first apps with Entity Framework Core. |
|
|
SIL.Harmony.Linq2db
linq2db integration for the Harmony CRDT library. |
|
|
EFCore.ComplexIndexes.PostgreSQL
PostgreSQL provider extensions for EFCore.ComplexIndexes — GIN, GiST, BRIN, SP-GiST, and Hash index support for complex type properties. |
|
|
EFCore.ComplexIndexes.SqlServer
SQL Server provider extensions for EFCore.ComplexIndexes — clustered, covering (INCLUDE), online-built, and fill-factor index options for complex type properties. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 5.3.0 | 205 | 9/5/2026 |
| 5.2.0 | 158 | 9/5/2026 |
| 5.1.0 | 132 | 9/5/2026 |
| 5.0.3 | 780 | 8/15/2026 |
| 5.0.2 | 135 | 8/15/2026 |
| 5.0.1 | 165 | 8/10/2026 |
| 5.0.0 | 125 | 8/10/2026 |
| 4.0.0 | 2,703 | 6/14/2026 |
| 3.1.5 | 7,992 | 6/5/2026 |
| 3.1.0 | 150 | 6/4/2026 |
| 3.0.0 | 135 | 6/3/2026 |
| 2.0.5 | 445 | 5/14/2026 |
| 2.0.2 | 1,098 | 2/14/2026 |
| 1.0.2 | 141 | 2/12/2026 |
| 1.0.1 | 136 | 2/12/2026 |