EFCore.ComplexIndexes.SqlServer 5.6.0

dotnet add package EFCore.ComplexIndexes.SqlServer --version 5.6.0
                    
NuGet\Install-Package EFCore.ComplexIndexes.SqlServer -Version 5.6.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.ComplexIndexes.SqlServer" Version="5.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EFCore.ComplexIndexes.SqlServer" Version="5.6.0" />
                    
Directory.Packages.props
<PackageReference Include="EFCore.ComplexIndexes.SqlServer" />
                    
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.ComplexIndexes.SqlServer --version 5.6.0
                    
#r "nuget: EFCore.ComplexIndexes.SqlServer, 5.6.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.ComplexIndexes.SqlServer@5.6.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.ComplexIndexes.SqlServer&version=5.6.0
                    
Install as a Cake Addin
#tool nuget:?package=EFCore.ComplexIndexes.SqlServer&version=5.6.0
                    
Install as a Cake Tool

EFCore.ComplexIndexes.SqlServer

On EF Core 11 or later? This package supports EF Core 10 only. From 5.5.0 it throws when you register it on any other major version. Its successor for EF Core 11 is a new package, CodoMetis.EFCore.SchemaKit, tracked in #37.

SQL Server index options for EFCore.ComplexIndexes. This package brings the core package with it.

You get SQL Server's index options on indexes over complex properties:

  • Clustered / nonclustered control
  • Covering indexes (INCLUDE)
  • Online index builds (ONLINE = ON)
  • Fill factor
  • Sort in tempdb
  • Data compression (ROW / PAGE)

Setup

Migrations need no setup. Every option goes into the migration as a SQL Server annotation that EF Core's own SQL Server generator writes. Install the package, declare your indexes, and run dotnet ef migrations add.

There is one optional call. Database.EnsureCreated(), GenerateCreateScript() and the pending-model-changes check in Migrate() use your context's own services, which the automatic design-time registration never reaches. Without a registration, EnsureCreated() creates the tables and none of the indexes, with no error. Register the package once so these see your complex indexes too:

options.UseSqlServer(connectionString).UseSqlServerComplexIndexes();

If you build your own internal service provider, call AddSqlServerComplexIndexes() on your IServiceCollection instead.

Usage

builder.ComplexProperty(x => x.Email, c =>
    c.Property(x => x.Value).HasColumnName("email"));

builder.HasComplexIndex(x => x.Email.Value, ix => ix
    .IsUnique()
    .HasName("ux_person_email")
    .IncludeProperties("Name")     // property paths resolve to columns
    .IsCreatedOnline()             // ONLINE = ON
    .HasFillFactor(80));
// CREATE UNIQUE INDEX [ux_person_email] ON [person] ([email])
//   INCLUDE ([name]) WITH (FILLFACTOR = 80, ONLINE = ON);

The builder also has IsClustered(), SortInTempDb() and UseDataCompression(DataCompressionType.Page).

Filtered indexes (filter:) and DbOrder.Desc need nothing from this package, because EF Core's own index operation carries both.

A filter can name properties instead of columns: migrations add turns filter: "{DeletedAt} IS NULL" into [deleted_at] IS NULL.

Deliberate rejections

If you declare something SQL Server can't express, dotnet ef migrations add fails and names the problem, instead of writing a migration that would fail when it runs:

  • Expression parts. SQL Server has no DDL for an index on an expression. Store the expression in a persisted computed column and index that column instead.
  • DbOrder.NullsFirst / NullsLast. T-SQL has no NULLS FIRST/NULLS LAST.
  • A clustered index with INCLUDE columns. Included columns belong to nonclustered indexes. A clustered index already stores every column.
  • A clustered filtered index. A filtered index must be nonclustered.
  • A second clustered index on a table. Usually the primary key already holds the clustered slot: SQL Server clusters the primary key unless you declare HasKey(...).IsClustered(false). So that is normally what a clustered complex index collides with.

Documentation

Changelog

This package's changelog, or the root changelog covering all three packages.

MIT licensed.

Product 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. 
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
5.6.0 62 10/23/2026
5.5.0 259 10/20/2026
5.4.1 1,549 9/22/2026
5.4.0 226 9/22/2026
5.3.0 143 9/5/2026
5.2.0 153 9/5/2026
5.1.0 132 9/5/2026
5.0.3 223 8/15/2026
5.0.2 136 8/15/2026
5.0.1 120 8/10/2026
5.0.0 126 8/10/2026

Migrations keep the indexes and constraints a column change used to drop or rebuild: Changed answers in 5.6.0 (https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/blob/v5.6.0/docs/upgrading.md#changed-answers-in-560).

Changed answers

- Migrations: moving a table into the default schema keeps the complex indexes and constraints on it. They were dropped and created again, and on PostgreSQL, where Npgsql leaves such a table in its schema, the migration failed (42P01) (#185 (https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/issues/185), Upgrading (https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/blob/v5.6.0/docs/upgrading.md#a-move-into-the-default-schema-keeps-what-is-on-the-table)).
- Migrations: on SQL Server, changing only a column's default value, default SQL or comment keeps the complex indexes on it. Each was dropped and created again around the change, which issues no ALTER COLUMN (#186 (https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/issues/186), Upgrading (https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/blob/v5.6.0/docs/upgrading.md#a-default-or-comment-change-keeps-the-index-on-sql-server)).
- Migrations: renaming a column keeps the complex indexes, exclusion constraints and temporal constraints that read it, and renames a default name that includes the column. They were dropped and created again around the rename. SQL Server still re-creates an index whose filter reads the column (#182 (https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/issues/182), Upgrading (https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/blob/v5.6.0/docs/upgrading.md#a-column-rename-keeps-what-reads-the-column)).

Fixed

- Indexes: on SQL Server, a complex index whose filter or INCLUDE entry names a column in another case, [note] for Note, is dropped before an ALTER COLUMN or rename of that column and created after it, where the migration failed at apply, since SQL Server rejects either while the index exists (Msg 5074 for ALTER COLUMN) (#189 (https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/issues/189)).

Full changelog: https://github.com/CaffeinatedCoder/EFCore.ComplexIndexes/blob/v5.6.0/src/EFCore.ComplexIndexes.SqlServer/CHANGELOG.md