efcore-patterns
Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.
By aaronontheweb · 1,448 installs
npx skills add aaronontheweb/dotnet-skills --skill efcore-patterns
Source repository · Upstream listing
Entity Framework Core Patterns
When to Use This Skill
Use this skill when:
Setting up EF Core in a new project
Optimizing query performance
Managing database migrations
Integrating EF Core with .NET Aspire
Debugging change tracking issues
Loading multiple navigation collections efficiently (query splitting)
Core Principles
1. NoTracking by Default Most queries are read only; opt in to tracking
2. Never Edit Migrations Manually Always use CLI commands
3. Dedicated Migration Service Separate migration execution from application startup
4. ExecutionStrategy for Retries Handle transient database failures
5. Explicit Updates When NoTracking, explicitly mark entities for update
Pattern 1: NoTracking by Default
Configure your DbContext to disable change tracking by default. This improves performance for read heavy workloads.
When NoTracking is Active
Read only queries work normally:
Writes require explicit handling:
When to Use Tracking
Scenario Use Tracking? Why
Display data in UI No Read only, no updates
API GET endpoints No Returning data, no mutations
Update single entity Yes or explicit Update() Need to save changes
Complex update with navigation Yes Tracking handles relationships
Batch operations No + ExecuteUpdate More efficient
Explicit Add/Update Pattern
Pattern 2: Never Edit Migrations Manually
CRITICAL: Always use EF Core CLI commands to manage migrations. Never:
Manually edit migration files (except for custom SQL in Up() / Down() )
Delete migration files directly
Rename migration files
Copy migrations between projects
Creating Migrations
Removing Migrations
Applying Migrations
Generating SQL Scripts
Pattern 3: Dedicated Migration Service with Aspire
Separate migration execution from your main application using a dedicated migration service. This ensures:
Migrations complete before the app starts
Clean separation of concerns
Controlled seeding in test environments
Project Structure
MigrationService Program.cs
MigrationWorker.cs
AppHost Configuration
Pattern 4: ExecutionStrategy for Transient Failures
Always use CreateExecutionStrategy() for operations that might fail transiently:
Important: You cannot use CreateExecutionStrategy() with user initiated transactions. If you need transactions with retry:
Pattern 5: Bulk Operations with ExecuteUpdate/ExecuteDelete
For bulk operations, use EF Core 7+ ExecuteUpdateAsync and ExecuteDeleteAsync instead of loading entities:
Common Pitfalls
1. Forgetting to Update When NoTracking
2. N+1 Query Problem
3. Tracking Conflicts with Multiple DbContext Instances
4. Not Using Async Consistently
5. Querying Inside Loops
DbContext Lifetime in DI
ASP.NET Core (Scoped by Default)
Background Services (Create Scope)
Actors / Long Lived Objects (Factory Pattern)
Pattern 6: Query Splitting to Prevent Cartesian Explosion
When you load multiple navigation collections via Include() , EF Core generates a single query that can cause cartesian explosion. If you have 10 orders with 10 items each, you get 100 rows instead of 10 + 10.
Global Configuration (Recommended for Most Cases)
Enable query splitting globally in your DbContext configuration:
Per Query Override
Use single query when you know it's more efficient:
Trade offs
Behavior Pros Cons
SplitQuery No cartesian explosion, better for large collections Multiple round trips, potential consistency issues
SingleQuery Single round trip, transactional consistency Cartesian explosion with multiple collections
Recommendation : Default to SplitQuery globally, override with AsSingleQuery() for specific queries where single query is known to be better.
When to Prefer SingleQuery
Small, well understood navigation graphs (2 3 levels)
Queries where all related data is always needed
Performance critical paths where round trip cost is lower than cartesian explosion
When to Prefer SplitQuery
Large or unpredictable navigation graphs
Many to many relationships
Queries loading collections that may not all be needed
Testing with EF Core
In Memory Provider (Unit Tests Only)
Real Database with TestContainers (Integration Tests)
See the testcontainers integration tests skill for proper database testing.