Claude-skill-registry dotnet-ef-migrations
Master Entity Framework Core migrations with code-first approach, migration strategies, data seeding, rollback procedures, and production deployment patterns. Use when managing database schema changes in .NET applications.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dotnet-ef-migrations" ~/.claude/skills/majiayu000-claude-skill-registry-dotnet-ef-migrations && rm -rf "$T"
manifest:
skills/data/dotnet-ef-migrations/SKILL.mdsource content
.NET Entity Framework Core Migrations
Master EF Core migrations for .NET 8+ with code-first database management.
Setup
# Install EF Core tools dotnet tool install --global dotnet-ef dotnet tool update --global dotnet-ef # Add EF Core packages dotnet add package Microsoft.EntityFrameworkCore.Design dotnet add package Microsoft.EntityFrameworkCore.SqlServer # Or PostgreSQL dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
DbContext Configuration
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Order> Orders { get; set; } public DbSet<Customer> Customers { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); // Global query filters modelBuilder.Entity<Order>().HasQueryFilter(o => !o.IsDeleted); base.OnModelCreating(modelBuilder); } } // Entity Configuration public class OrderConfiguration : IEntityTypeConfiguration<Order> { public void Configure(EntityTypeBuilder<Order> builder) { builder.ToTable("Orders"); builder.HasKey(o => o.Id); builder.Property(o => o.OrderNumber) .IsRequired() .HasMaxLength(20); builder.Property(o => o.TotalAmount) .HasColumnType("decimal(18,2)"); builder.HasIndex(o => o.OrderNumber) .IsUnique(); builder.HasOne(o => o.Customer) .WithMany(c => c.Orders) .HasForeignKey(o => o.CustomerId) .OnDelete(DeleteBehavior.Restrict); builder.HasMany(o => o.Items) .WithOne(i => i.Order) .HasForeignKey(i => i.OrderId) .OnDelete(DeleteBehavior.Cascade); } }
Migration Commands
# Add migration dotnet ef migrations add InitialCreate dotnet ef migrations add AddOrderStatus dotnet ef migrations add UpdateCustomerEmail # Update database dotnet ef database update # Update to specific migration dotnet ef database update AddOrderStatus # Rollback migration dotnet ef database update PreviousMigration # Remove last migration (if not applied) dotnet ef migrations remove # Generate SQL script dotnet ef migrations script dotnet ef migrations script InitialCreate AddOrderStatus # Generate idempotent script dotnet ef migrations script --idempotent # List migrations dotnet ef migrations list # Drop database dotnet ef database drop --force
Migration File Structure
public partial class AddOrderStatus : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<int>( name: "Status", table: "Orders", type: "int", nullable: false, defaultValue: 0); migrationBuilder.CreateIndex( name: "IX_Orders_Status", table: "Orders", column: "Status"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropIndex( name: "IX_Orders_Status", table: "Orders"); migrationBuilder.DropColumn( name: "Status", table: "Orders"); } }
Data Seeding
// In OnModelCreating protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<OrderStatus>().HasData( new OrderStatus { Id = 1, Name = "Pending" }, new OrderStatus { Id = 2, Name = "Processing" }, new OrderStatus { Id = 3, Name = "Shipped" }, new OrderStatus { Id = 4, Name = "Delivered" } ); } // Or in migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.InsertData( table: "OrderStatuses", columns: new[] { "Id", "Name" }, values: new object[,] { { 1, "Pending" }, { 2, "Processing" }, { 3, "Shipped" }, { 4, "Delivered" } }); }
Custom SQL in Migrations
protected override void Up(MigrationBuilder migrationBuilder) { // Execute raw SQL migrationBuilder.Sql(@" CREATE INDEX IX_Orders_CreatedAt_Status ON Orders (CreatedAt, Status) WHERE IsDeleted = 0 "); // Create stored procedure migrationBuilder.Sql(@" CREATE PROCEDURE GetOrdersByCustomer @CustomerId uniqueidentifier AS BEGIN SELECT * FROM Orders WHERE CustomerId = @CustomerId AND IsDeleted = 0 END "); // Create view migrationBuilder.Sql(@" CREATE VIEW vw_ActiveOrders AS SELECT o.*, c.Name AS CustomerName FROM Orders o INNER JOIN Customers c ON o.CustomerId = c.Id WHERE o.IsDeleted = 0 "); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.Sql("DROP VIEW IF EXISTS vw_ActiveOrders"); migrationBuilder.Sql("DROP PROCEDURE IF EXISTS GetOrdersByCustomer"); migrationBuilder.Sql("DROP INDEX IF EXISTS IX_Orders_CreatedAt_Status ON Orders"); }
Production Deployment
// Program.cs - Apply migrations on startup (development only) if (app.Environment.IsDevelopment()) { using var scope = app.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService<AppDbContext>(); await db.Database.MigrateAsync(); } // Production - Generate SQL scripts // dotnet ef migrations script --idempotent --output migrations.sql // Review and apply scripts manually // Or use migration bundles (EF Core 6+) // dotnet ef migrations bundle --self-contained -r linux-x64 // ./efbundle --connection "Server=..."
Migration Strategies
1. Script-Based (Recommended for Production)
# Generate script dotnet ef migrations script --idempotent --output migration.sql # Review script # Apply via SQL tool or deployment pipeline
2. Runtime Migration
// Only in development/staging await dbContext.Database.MigrateAsync();
3. Migration Bundles
# Create bundle dotnet ef migrations bundle --self-contained -r linux-x64 # Deploy and run ./efbundle --connection "Server=prod;Database=MyDb;..."
Handling Migration Conflicts
// Multiple developers scenario: // 1. Pull latest code git pull origin main // 2. If migration conflicts, remove your migration dotnet ef migrations remove // 3. Create new migration dotnet ef migrations add YourFeature // 4. Test migration dotnet ef database update
Rolling Back Migrations
# Rollback to specific migration dotnet ef database update PreviousMigration # Rollback all dotnet ef database update 0 # Generate rollback script dotnet ef migrations script CurrentMigration PreviousMigration --output rollback.sql
Best Practices
- Never Modify Applied Migrations - Create new migration instead
- Review Generated Migrations - Check SQL before applying
- Test Migrations - Test on copy of production data
- Use Transactions - Migrations run in transactions by default
- Backup Before Migration - Always backup production database
- Idempotent Scripts - Use
flag for production--idempotent - Version Control - Commit migrations with code changes
- Data Migration - Separate data migrations from schema migrations
Common Patterns
// Adding nullable column then making it required protected override void Up(MigrationBuilder migrationBuilder) { // Step 1: Add nullable column migrationBuilder.AddColumn<string>( name: "Email", table: "Customers", nullable: true); // Step 2: Update existing rows migrationBuilder.Sql("UPDATE Customers SET Email = 'unknown@example.com' WHERE Email IS NULL"); // Step 3: Make column required migrationBuilder.AlterColumn<string>( name: "Email", table: "Customers", nullable: false); } // Renaming column migrationBuilder.RenameColumn( name: "OldName", table: "TableName", newName: "NewName"); // Changing column type migrationBuilder.AlterColumn<decimal>( name: "Price", table: "Products", type: "decimal(18,2)", nullable: false, oldClrType: typeof(float));