Database Migrations

Managing database schema changes with Entity Framework Core — running, creating, reverting, and auto-applying migrations.

Overview

ZapTicket uses Entity Framework Core (EF Core) for database migrations. Migrations are C# files that describe schema changes — they're committed to source control and applied to the database either at startup or manually via CLI.

Running Migrations

Option 1: Auto-Run on Startup

Set the RUN_MIGRATIONS_ON_STARTUP environment variable to true. The backend will apply all pending migrations when it starts:

Environment Variable
RUN_MIGRATIONS_ON_STARTUP=true

This is the recommended approach for development and simple deployments. The application checks for pending migrations and applies them before accepting traffic.

💡The startup migration runner is idempotent — if all migrations are already applied, it completes instantly. It only applies new migrations that haven't been run yet.

Option 2: Manual CLI

For production deployments where you want more control, run migrations manually before deploying the new code:

Apply Migrations via CLI
cd backend

# Apply all pending migrations
dotnet ef database update

# Apply to a specific connection string
dotnet ef database update --connection "Host=prod-db.example.com;Database=zapticket;Username=admin;Password=..."

# See what would be applied (dry run)
dotnet ef migrations list
⚠️When running migrations against production, always review the SQL that will be generated first using dotnet ef migrations script (see below). This prevents surprises from destructive schema changes.

Creating New Migrations

When you modify an entity class or DbContext configuration, create a migration to capture the schema diff:

Create a Migration
cd backend

# Create a new migration
dotnet ef migrations add AddTicketPriority

# This creates:
# Migrations/
#   20240115120000_AddTicketPriority.cs          (up/down logic)
#   20240115120000_AddTicketPriority.Designer.cs (snapshot)

Best practices for naming migrations:

  • Use descriptive PascalCase names: AddTicketPriority, CreateMessagesTable
  • Prefix with the action: Add, Create, Remove, Rename, Update
  • Keep names short but clear about what changed

Generating SQL Scripts

For auditing or manual application in production, generate the raw SQL:

Generate SQL
# Generate SQL for all pending migrations
dotnet ef migrations script --idempotent -o migrations.sql

# Generate SQL between two specific migrations
dotnet ef migrations script PreviousMigration CurrentMigration -o changes.sql

# Generate from the beginning (full schema)
dotnet ef migrations script 0 -o full-schema.sql
💡The --idempotent flag wraps each migration in a conditional check, making it safe to run multiple times. Use this for production scripts.

Reverting Migrations

To undo the most recent migration (before it's been pushed/deployed):

Revert Last Migration
# Revert the database to the previous migration
dotnet ef database update PreviousMigrationName

# Remove the migration file (only if not yet applied to production)
dotnet ef migrations remove

To revert to a specific earlier migration:

Revert to Specific Point
# List migrations to find the target
dotnet ef migrations list

# Revert to a specific migration (undoes everything after it)
dotnet ef database update 20240110090000_CreateTenantsTable
🚨Reverting migrations that have been applied in production can cause data loss. If a migration dropped a column or table, reverting won't restore the data. Always back up before reverting in production.

Migration File Structure

Example Migration
public partial class AddTicketPriority : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<string>(
            name: "Priority",
            table: "Tickets",
            type: "text",
            nullable: false,
            defaultValue: "medium");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "Priority",
            table: "Tickets");
    }
}

The Up method applies the change. The Down method reverts it. Always implement both for reversibility.

Deployment Workflow

Recommended Production Flow
1. Develop locally with RUN_MIGRATIONS_ON_STARTUP=true
2. Create migration: dotnet ef migrations add <Name>
3. Test locally (auto-applies on restart)
4. Review generated SQL: dotnet ef migrations script --idempotent
5. Commit migration files to source control
6. Deploy:
   - Option A: Set RUN_MIGRATIONS_ON_STARTUP=true (simple)
   - Option B: Run dotnet ef database update manually, then deploy code