In this lesson, you will learn how to evolve your database schema in a production environment without forcing your application offline.
When working on enterprise-scale systems, the standard dotnet ef database update approach is often insufficient because it can lock tables or cause breaking changes during the deployment window. To achieve zero-downtime, we employ the Expand-Contract (or Parallel Change) pattern.
The Expand-Contract Pattern
The core philosophy is to never make a change that breaks compatibility with the existing version of your application. Instead, we break migrations into three distinct phases:
- Expand: Update the database schema to support both the old and new structures simultaneously. For example, if you are renaming a column from
CustomerNametoFullName, you add theFullNamecolumn while keepingCustomerNameintact. - Migrate: Update your application code to start reading from both columns, but writing to the new one. Use a data migration task (or a custom
ValueConverter) to sync existing records. - Contract: Once all instances of the old application code are decommissioned, run a final migration to drop the obsolete
CustomerNamecolumn.
Implementation Strategy
In EF Core, you manage these transitions by creating incremental migrations that are additive. To ensure the application remains stable during the "Expand" phase, configure your entity mapping to handle the transitional state:
// Step 1: The Expand phase mapping
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.Property(c => c.FullName)
.HasColumnName("FullName"); // New column
// Maintain old property temporarily for compatibility
modelBuilder.Entity<Customer>()
.Property(c => c.CustomerName)
.HasColumnName("CustomerName");
}
By decoupling the database deployment from the application code deployment, you ensure that at any point during the rollout, either the old version or the new version of your service can function against the database. If a deployment fails, you don't have to roll back the database, as the schema remains backward compatible.
Key Takeaway: Never execute a destructive schema change (dropping or renaming) in a single step; always expand the schema to support both old and new code, then contract once the transition is complete.