EF Core Migrations
Version control for your database schema with Entity Framework Core.
By EMEPublished: February 20, 2025
entity frameworkmigrationsdatabase schemaaspnet core
A Simple Analogy
Migrations are like version control for databases. Each change creates a new version file, you can move forward or backward in time, and team members sync the same schema.
Why Migrations?
- Version control: Track all schema changes in Git
- Team coordination: Everyone applies same changes
- Production deployment: Reliable schema updates
- Rollback capability: Undo changes if needed
- Documentation: Migration history explains why
Create Migration
// Define entities
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("connection-string");
}
}
// Create migration
// dotnet ef migrations add InitialCreate
// Migration file generated automatically:
// Migrations/20250220120000_InitialCreate.cs
Migration Structure
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(nullable: false),
Email = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(name: "Users");
}
}
Applying Migrations
// In Program.cs
var app = builder.Build();
// Auto-apply migrations on startup
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Database.Migrate(); // Applies all pending migrations
}
app.Run();
# Manual application
dotnet ef database update
# Apply specific migration
dotnet ef database update InitialCreate
# Rollback to previous
dotnet ef database update PreviousMigration
# List migrations
dotnet ef migrations list
Adding Columns
// Add column to entity
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime CreatedAt { get; set; } // NEW
}
// Create migration
// dotnet ef migrations add AddCreatedAtToUsers
// Generated migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "CreatedAt",
table: "Users",
nullable: false,
defaultValue: DateTime.UtcNow);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn("CreatedAt", "Users");
}
Complex Migrations
public partial class SplitNameColumn : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// Add new columns
migrationBuilder.AddColumn<string>(
name: "FirstName",
table: "Users",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "LastName",
table: "Users",
nullable: true);
// SQL to split existing name
migrationBuilder.Sql(
@"UPDATE Users SET FirstName = SUBSTRING(Name, 1, CHARINDEX(' ', Name)-1),
LastName = SUBSTRING(Name, CHARINDEX(' ', Name)+1, LEN(Name))");
// Make new columns non-nullable
migrationBuilder.AlterColumn<string>(
name: "FirstName",
table: "Users",
nullable: false);
migrationBuilder.AlterColumn<string>(
name: "LastName",
table: "Users",
nullable: false);
// Drop old column
migrationBuilder.DropColumn("Name", "Users");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// Reverse operations...
}
}
Best Practices
- Create frequently: One feature = one migration
- Name descriptively:
AddUserRoleColumn, notMigration2 - Keep idempotent: Safe to run multiple times
- Test rollbacks: Verify Down() works
- Review generated code: Ensure correctness
Related Concepts
- Database snapshots
- Initial data seeding
- Shadow properties
- Fluent API configuration
Summary
EF Core Migrations provide version control for database schemas. Master creating, applying, and rolling back migrations to maintain consistency across development and production environments.