liveBook · Manning

Database migration

As explained in Building Distributed Applications that Work

A database migration is a controlled change to a database schema or associated database objects across application versions.

A database migration is a named, timestamped record of a schema change. For a SQL-backed application, migrations provide a way to update table structure as application versions change. The database records which migrations it has received, so a later deployment applies only the outstanding changes, in order. section 9.1.3

In an Aspire-hosted online shop, the API service depends on a SQL database. Configure migrations as part of deployment so a new application version can bring its database schema up to date automatically. Plan this alongside availability: the deployment setup is intended to apply changes without downtime. section 9.1 section 9.1.3

To establish the migration workflow:

  • Add Microsoft.EntityFrameworkCore.Design to the API service. Ensure an Entity Framework database context and at least one entity type exist, and install the Entity Framework CLI tools. section 9.1.3
  • Generate an initial migration with `dotnet ef migrations add InitialCreate`. On first deployment, its schema is applied and its name is recorded by the database. section 9.1.3
  • After changing an entity, generate another migration. For example, adding an optional address to an order and running `dotnet ef migrations add AddOrderAddress` produces a migration that adds a nullable column to `Orders`. section 9.1.3
protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AddColumn<string>(
        name: "Address",
        table: "Orders",
        type: "nvarchar(max)",
        nullable: true);
}

The generated `Up` method shows the database operation behind the order-address change: it adds a nullable `Address` column to `Orders`. section 9.1.3

To run outstanding migrations when the service starts, a hosted `DataSeederService` creates a dependency-injection scope, resolves `ProductsDbContext`, and calls a migration method before seeding data. That method uses the database execution strategy to call `MigrateAsync`. section 9.1.3

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    using var scope = _serviceProvider.CreateScope();
    using var context = scope.ServiceProvider.GetRequiredService<ProductsDbContext>();

    await RunMigrationAsync(context, stoppingToken);
    SeedData(context);
}

The hosted-service flow resolves the database context, runs migrations, then seeds product data. section 9.1.3

Do not assume migrations will work on a database created with `EnsureCreated()`. That path does not create the table used to track migration versions; the database needs manual changes before migrations can update it. section 9.1.3

How Building Distributed Applications that Work covers it

How deep it goes
Works through migration creation, version tracking, generated schema changes, and startup execution; does not detail the manual repair for EnsureCreated databases.
Examples
Uses an online-shop API, an initial schema migration, an order-address column, and a hosted service that migrates before seeding.
What it assumes
Requires Entity Framework CLI tools, a database context, and an entity type; flags EnsureCreated databases as requiring manual preparation.
What you can do after
Apply it via Entity Framework migrations and an Aspire-hosted service to run outstanding schema changes during deployment.
How it connects
Connects Entity Framework migrations to SQL schemas, Aspire deployment, hosted services, execution strategies, and data seeding.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Building Distributed Applications that Work. The section links point to the parts of the book it draws on.