ASP.NET Core cookbook · Intermediate
Apply EF Core migrations safely during deployment
Calling Database.Migrate() at startup races between instances and needs schema permissions in the app.
Approach
Build a migration bundle in CI and run it as a separate deployment step (or Kubernetes Job) with a migration user, before the new app version starts. Keep migrations backward compatible with the running version.
Solution
# CI: generate an idempotent script for review, and a self-contained bundle to execute
dotnet ef migrations script --idempotent --output artifacts/migrate.sql --project src/Shop.Api
dotnet ef migrations bundle --self-contained -r linux-x64 --output artifacts/efbundle --project src/Shop.Api
# Deploy step (runs once, before rolling out the app)
./artifacts/efbundle --connection "$MIGRATIONS_CONNECTION_STRING"
// Design-time factory so `dotnet ef` can create the context without running the web host
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
public class ShopDbFactory : IDesignTimeDbContextFactory<ShopDb>
{
public ShopDb CreateDbContext(string[] args) =>
new(new DbContextOptionsBuilder<ShopDb>()
.UseSqlite(Environment.GetEnvironmentVariable("MIGRATIONS_CONNECTION_STRING") ?? "Data Source=design.db")
.Options);
}
public class ShopDb(DbContextOptions<ShopDb> options) : DbContext(options)
{
public DbSet<Product> Products => Set<Product>();
}
public class Product { public int Id { get; set; } public string Name { get; set; } = ""; }
Pitfalls
Migrate()inProgram.cswith several replicas starting at once.- Destructive migrations (drop/rename column) in the same release as the code change; expand, then contract.
- The app's runtime database user having DDL permissions.
Variations
- Review generated SQL in pull requests (
migrations script) for locking operations. - For PostgreSQL, create indexes concurrently with
migrationBuilder.Sql(...)andsuppressTransaction: true.
Further reading: Applying migrations
Reference: https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying
More ASP.NET Core recipes
- Accept file uploads with size and type limits
- Add OpenTelemetry traces and metrics
- Bind and validate options at startup
- Cache responses with output caching and evict by tag
- Call external APIs with typed HttpClients and resilience
- Configure CORS for a browser front end
- Expose liveness and readiness health checks
- Generate OpenAPI documents with Microsoft.AspNetCore.OpenApi