📌 Indexes in EF Core
Indexes are one of the most powerful tools for query performance tuning.
In Entity Framework Core (EF Core), indexes help:
⚡ Speed up lookups, filtering, and sorting🔒 Enforce uniqueness
🏢 Support multi-tenant and domain-specific rules
This guide walks through everything you need to know — from the basics to advanced scenarios — with real-world code samples and best practices.
✅ What is an Index in EF Core?
An index is a database object that allows the database to locate rows faster.
Use .HasIndex() → to create indexes via Fluent API Use .IsUnique() → to enforce uniqueness EF Core automatically creates indexes for foreign key columns✅ Defining Indexes in EF Core
| Type | Description |
|---|---|
| Basic Index | Creates a non-unique index on the Name column. |
| Unique Index | Ensures no two rows share the same Sku (enforced at the DB level). |
| Composite Index (Multi-Column) | Used when queries filter on multiple fields together. Example: WHERE Name='Laptop' AND Category='Electronics' |
| Shadow Property Index | Indexes a property not defined in the C# model. |
| Custom Index Name | Useful when aligning with existing database conventions. |
✅ Filtered Indexes (Manual SQL via Migrations)
EF Core does not support filtered indexes via Fluent API. Use raw SQL in migrations.
Common Use Cases:
- Enforce uniqueness only for active rows (IsActive = 1)
- Allow multiple NULLs, enforce uniqueness when NOT NULL:
CREATE UNIQUE INDEX IX_Unique_Email
ON Users(Email)
WHERE Email IS NOT NULL;
✅ Data Integrity with Indexes
| Method | Purpose |
|---|---|
.HasIndex().IsUnique() |
Creates a unique index (good for queries) |
.HasAlternateKey() |
Creates a unique constraint (FK principal) |
👉 Use .HasAlternateKey() when the column is involved in relationships.
👉 Use .HasIndex().IsUnique() for query optimization + uniqueness.
✅ Performance Considerations
| ✔️ Benefits | ❌ Downsides |
|---|---|
|
|
👉 Best Practice: Only index columns you frequently filter, sort, or join on.
✅ Common Scenarios & EF Core Solutions
| Scenario | EF Core Code |
|---|---|
| Single property index | .HasIndex(p => p.Name) |
| Unique value | .HasIndex(p => p.Sku).IsUnique() |
| Composite index | .HasIndex(p => new { p.Name, p.Category }) |
| Unique + nullable column | migrationBuilder.Sql(...) (filtered index) |
| Shadow property index | .HasIndex("ShadowPropertyName") |
| Only one active record per user | migrationBuilder.Sql(... WHERE IsActive=1) |
| Custom index name | .HasDatabaseName("IX_Custom_Name") |
✅ Frequently Asked Questions (FAQs)
| Question | Answer |
|---|---|
| Can EF Core create multiple indexes on the same column? | ✅ Yes, but duplicates are prevented. |
| Does EF Core support index hints? | ❌ No. Use FromSqlRaw() for raw SQL with index hints. |
Difference between .HasIndex().IsUnique() and .HasAlternateKey():
| Feature | .HasIndex().IsUnique() | .HasAlternateKey() |
|---|---|---|
| Type | Unique Index | Unique Constraint |
| Used for FKs? | ❌ No | ✅ Yes |
| Performance | ✅ Optimized for queries | ❌ Slightly heavier |
| Identity/Relationships | ❌ Not entity identity | ✅ Used for identity & FKs |
🔄 Advanced Indexing Scenarios
| Feature | Support & Example |
|---|---|
| Clustered / Non-clustered | ✅ SQL Server (via migration SQL) |
| Covering Index (INCLUDE) | ✅ Use raw SQL (migrationBuilder.Sql(...)) |
| Partial Index | ✅ PostgreSQL via raw SQL |
| JSON Column Indexing | ✅ Provider-specific (Postgres, SQL Server) |
| Descending Index (EF 7+) | .IsDescending(true, false) |
📊 Quick Reference – Index Types
| Type | Supported? | Example |
|---|---|---|
| Single-column Index | ✅ Yes | .HasIndex(p => p.Name) |
| Composite Index | ✅ Yes | .HasIndex(p => new { p.Name, p.Category }) |
| Unique Index | ✅ Yes | .HasIndex(p => p.Sku).IsUnique() |
| Shadow Property Index | ✅ Yes | .HasIndex("Sku") |
| Custom Index Name | ✅ Yes | .HasDatabaseName("IX_Custom") |
| Filtered Index | ⚠️ Manual | migrationBuilder.Sql(...) |
| Covering Index | ⚠️ Manual | migrationBuilder.Sql(...) |
| Clustered Index | ⚠️ Manual | SQL Server migration SQL |
| Descending Index | ✅ EF 7+ | .IsDescending(true, false) |
| Partial Index | ⚠️ Manual | PostgreSQL raw SQL |
📌 Key Takeaways
- Use .HasIndex() → to define indexes
- Use .IsUnique() → to enforce uniqueness
- Use .HasAlternateKey() → when property acts as a principal key
- Use filtered indexes → for conditional uniqueness (nullable fields, active-only rows)
- Avoid over-indexing → balance read performance vs write efficiency
🔑 Final Thoughts
Indexes are easy to implement but extremely powerful for performance and integrity.
A solid indexing strategy can:
- ✅ Boost query performance
- ✅ Enforce domain-specific rules
- ✅ Scale with your data
👉 Golden Rule: Index what you query — not what you hope you might query.
Comments
Post a Comment