Overview
What is C# EF Core Migrations By Example?
C# EF Core Migrations By Example is a code-first tutorial series teaching experienced .NET developers how to manage database schema evolution using Entity Framework Core Migrations. Through 30 heavily annotated, self-contained examples, you will achieve 95% coverage of EF Core migration patterns—from basic DbContext setup to idempotent script generation and production startup migrations.
This tutorial assumes you are an experienced developer familiar with C#, .NET, and relational databases. If you are new to C# or EF Core, start with foundational .NET tutorials first.
Why By Example?
Philosophy: Show the code first, run it second, understand through direct interaction.
Traditional tutorials explain concepts then show code. By-example tutorials reverse this: every example is a working, runnable code snippet with inline annotations showing exactly what happens at each step—migration class structure, generated SQL, CLI command output, and common pitfalls.
Target Audience: Experienced developers who:
- Already know C# and .NET fundamentals
- Understand relational databases and SQL
- Prefer learning through working code rather than narrative explanations
- Want comprehensive reference material covering 95% of production migration patterns
Not For: Developers new to C# or databases. This tutorial moves quickly and assumes foundational knowledge.
What Does 95% Coverage Mean?
95% coverage means the depth and breadth of EF Core migration features needed for production work, not toy examples.
Included in 95% Coverage
- DbContext Setup: DbContext class, DbSet properties, OnModelCreating configuration
- Migration Lifecycle: Creating, applying, reverting, and removing migrations
- MigrationBuilder Operations: CreateTable, AddColumn, DropColumn, CreateIndex, AddForeignKey
- CLI Tooling:
dotnet ef migrations add,dotnet ef database update,dotnet ef migrations list - History Tracking:
__EFMigrationsHistorytable, migration snapshot, Designer.cs metadata - Schema Constraints: Unique constraints, NOT NULL with defaults, CHECK constraints
- Column Types: UUID primary keys, timestamp columns, enum storage, precision types
- Relationships: Foreign keys, cascade delete, composite indexes, junction tables
- Seed Data: HasData seeding, idempotent data setup
- Advanced Operations: Column rename patterns, multiple tables per migration, SQL scripts
- Production Patterns: Idempotent scripts, ASP.NET Core startup migration application
Excluded from 95% (the remaining 5%)
- EF Core Internals: Provider adapter implementation, connection pool mechanics
- Rare Edge Cases: Obscure feature combinations not used in typical production code
- Database-Specific: Vendor-specific SQL fragments outside standard EF Core patterns
- Legacy Features: Deprecated APIs from EF Core 1.x or 2.x
- Custom Migration Operations: Writing custom IMigrationsSqlGenerator implementations
Tutorial Structure
30 Examples in One Beginner Level
Sequential numbering: Examples 1-30 (unified reference system)
Distribution:
- Beginner (Examples 1-30): 0-40% coverage — DbContext setup, migration basics, schema operations, CLI tooling, production patterns
Rationale: EF Core Migrations is a focused tool. Thirty examples provide granular progression from initial setup to production-ready SQL generation without overwhelming maintenance burden.
Five-Part Example Format
Every example follows a mandatory five-part structure:
Part 1: Brief Explanation (2-3 sentences)
Answers:
- What is this concept or pattern?
- Why does it matter in production code?
- When should you use it?
Part 2: Mermaid Diagram (when appropriate)
Included when (~40% of examples):
- Migration lifecycle has multiple stages
- Relationships between files need visualization
- CLI command flow is non-obvious
- Database schema relationships require illustration
Skipped when:
- Simple single-operation commands
- Straightforward column type changes
- Trivial additive migrations
Diagram requirements:
- Use color-blind friendly palette: Blue
#0173B2, Orange#DE8F05, Teal#029E73, Purple#CC78BC, Brown#CA9161 - Vertical orientation (mobile-first)
- Clear labels on all nodes and edges
- Comment syntax:
%%(NOT%%{ }%%)
Part 3: Heavily Annotated Code
Core requirement: Every significant line must have an inline comment.
Comment annotations use // => notation for C# and -- => for SQL:
migrationBuilder.CreateTable( // => Generates CREATE TABLE SQL statement
name: "users", // => Table name in the database
columns: table => new // => Lambda defines column set
{
id = table.Column<Guid>( // => Column named "id" of type uuid
type: "uuid", // => => Maps to PostgreSQL UUID type
nullable: false), // => => NOT NULL constraint applied
},
constraints: table => // => Lambda defines table-level constraints
{
table.PrimaryKey( // => Adds PRIMARY KEY constraint
"pk_users", x => x.id); // => => Constraint name and key column
});Required annotations:
- Migration builder calls: Document what SQL each method generates
- Column configurations: Show resulting SQL column definitions
- CLI commands: Show expected terminal output
- File paths: Note where generated files appear on disk
- Side effects: Document database changes, snapshot updates
Part 4: Key Takeaway (1-2 sentences)
Purpose: Distill the core insight to its essence.
Part 5: Why It Matters (50-100 words)
Purpose: Connect the pattern to real production consequences.
Self-Containment Rules
Critical requirement: Examples must be copy-paste-runnable within their chapter scope.
Beginner Level Self-Containment
Rule: Each example is completely standalone.
Requirements:
- Full class definitions with namespaces
- All necessary using statements
- Helper types defined in-place when needed
- No references to previous examples
- Runnable with
dotnet efCLI
Golden rule: If you delete all other examples, this example should still execute.
How to Use This Tutorial
Prerequisites
Before starting, ensure you have:
- .NET 8+ SDK installed
dotnet-efglobal tool installed (dotnet tool install --global dotnet-ef)- PostgreSQL (or your preferred database) running
- Basic C# and .NET knowledge
- Basic SQL and relational database knowledge
Running Examples
All CLI examples assume a project with EF Core configured:
# Install EF Core global tool
dotnet tool install --global dotnet-ef
# Create a migration
dotnet ef migrations add InitialCreate
# Apply migrations to database
dotnet ef database update
# Generate SQL script
dotnet ef migrations script --output migration.sqlLearning Path
For experienced .NET developers new to EF Core Migrations:
- Work through all 30 examples sequentially
- Run each CLI command in a test project
- Inspect generated migration files alongside examples
For quick reference:
- Use example numbers as reference (e.g., "See Example 9 for applying migrations")
- Search for specific patterns (Ctrl+F for "cascade", "unique", "HasData")
- Copy-paste examples as starting points for your migrations
Coverage Progression
- After Example 15 (40% coverage): Can create and apply basic migrations
- After Example 25 (70% coverage): Can handle most production schema operations
- After Example 30 (95% coverage): Expert-level EF Core migration mastery
Code Annotation Philosophy
Every example uses educational annotations to show exactly what happens:
// Migration class declaration
public partial class InitialCreate : Migration // => Partial class allows Designer.cs metadata
{
protected override void Up(MigrationBuilder migrationBuilder)
{ // => Up() runs on dotnet ef database update
migrationBuilder.CreateTable( // => Generates CREATE TABLE statement
name: "products", // => => Table name: products
columns: table => new // => => Column definitions
{
id = table.Column<int>( // => Column: id INTEGER
nullable: false), // => => NOT NULL constraint
});
}
}Annotations show:
- Generated SQL for each MigrationBuilder call
- CLI output from
dotnet efcommands - File system effects (files created, modified, or deleted)
- Database side effects (schema changes, constraint creation)
- Common pitfalls and edge cases
Quality Standards
Every example in this tutorial meets these standards:
- Self-contained: Copy-paste-runnable within chapter scope
- Annotated: Every significant line has an inline comment
- Tested: All code examples verified against real EF Core behavior
- Production-relevant: Real-world patterns, not toy examples
- Accessible: Color-blind friendly diagrams, clear structure
Next Steps
Ready to start? Begin with Beginner Examples (1-30).
Feedback and Contributions
Found an issue? Have a suggestion? This tutorial is part of the ayokoding-web learning platform. Check the repository for contribution guidelines.
Examples by Level
Beginner (Examples 1–30)
- Example 1: DbContext Setup
- Example 2: First Migration (dotnet ef migrations add)
- Example 3: Up() and Down() Methods
- Example 4: MigrationBuilder.CreateTable
- Example 5: MigrationBuilder.AddColumn
- Example 6: MigrationBuilder.DropColumn
- Example 7: MigrationBuilder.CreateIndex
- Example 8: MigrationBuilder.AddForeignKey
- Example 9: Applying Migrations (dotnet ef database update)
- Example 10: Reverting Migrations (dotnet ef database update PreviousMigration)
- Example 11: Removing Last Migration (dotnet ef migrations remove)
- Example 12: Listing Migrations (dotnet ef migrations list)
- Example 13: __EFMigrationsHistory Table
- Example 14: Migration Snapshot File
- Example 15: Designer.cs Metadata
- Example 16: Adding Unique Constraints
- Example 17: NOT NULL with Default Values
- Example 18: UUID/GUID Primary Keys
- Example 19: Timestamp Columns with Defaults
- Example 20: Enum Storage Patterns
- Example 21: CHECK Constraints
- Example 22: Composite Indexes
- Example 23: Junction Tables (Many-to-Many)
- Example 24: Seed Data with HasData
- Example 25: Multiple Tables in One Migration
- Example 26: Cascade Delete Configuration
- Example 27: Column Rename (Safe Pattern)
- Example 28: Generating SQL Script (dotnet ef migrations script)
- Example 29: Idempotent Script Generation
- Example 30: ASP.NET Core Startup Migration
Intermediate (Examples 31–60)
- Example 31: Data Seeding with HasData
- Example 32: Custom SQL in Migrations (migrationBuilder.Sql)
- Example 33: Transaction Control in Migrations
- Example 34: Multiple DbContexts in One Project
- Example 35: Migration Bundles (dotnet ef migrations bundle)
- Example 36: Scaffold from Existing DB (dotnet ef dbcontext scaffold)
- Example 37: Column Rename vs Drop/Create (Safe Pattern)
- Example 38: Owned Types (Value Objects)
- Example 39: Value Converters
- Example 40: Table Splitting
- Example 41: TPH Inheritance (Table Per Hierarchy)
- Example 42: TPT Inheritance (Table Per Type)
- Example 43: TPC Inheritance (Table Per Concrete Type)
- Example 44: Migration Squashing
- Example 45: Idempotent Scripts
- Example 46: Connection Resiliency in Migrations
- Example 47: Creating Views
- Example 48: Creating Materialized Views (raw SQL)
- Example 49: Trigger Functions (raw SQL)
- Example 50: Stored Procedures (raw SQL)
- Example 51: JSON Columns (ToJson)
- Example 52: Temporal Tables
- Example 53: Full-Text Search Setup
- Example 54: Migration Testing with xUnit
- Example 55: Test Database Setup with Testcontainers
- Example 56: Table Partitioning (raw SQL)
- Example 57: GIN Index for JSONB (raw SQL)
- Example 58: Composite Primary Keys
- Example 59: HiLo Sequence for ID Generation
- Example 60: IDesignTimeDbContextFactory
Advanced (Examples 61–85)
- Example 61: Custom Migration Operations
- Example 62: IDesignTimeDbContextFactory Deep Dive
- Example 63: Zero-Downtime Column Addition
- Example 64: Zero-Downtime Column Removal (3-Phase)
- Example 65: Zero-Downtime Table Rename
- Example 66: Large Table Migration with Batched Updates
- Example 67: Online Index Creation (CONCURRENTLY)
- Example 68: Data Backfill Pattern
- Example 69: EF Core Migrations in CI/CD Pipeline
- Example 70: Migration Rollback Testing with xUnit
- Example 71: Blue-Green Deployment Migrations
- Example 72: Feature Flag Migration Pattern
- Example 73: Multi-Tenant Schema Migration
- Example 74: Migration with pgcrypto Encryption
- Example 75: Audit Trail Table Migration
- Example 76: Soft Delete Schema Pattern
- Example 77: Compiled Models for Startup Performance
- Example 78: JSON Columns Deep Dive (ToJson)
- Example 79: Temporal Tables Deep Dive
- Example 80: Raw SQL Migrations with EF Core
- Example 81: ASP.NET Core Integration Patterns
- Example 82: Migration Performance Benchmarking
- Example 83: Schema Drift Detection
- Example 84: Production Migration Checklist
- Example 85: Migration Monitoring and Health Checks
Last updated March 26, 2026