Skip to main content

Command Palette

Search for a command to run...

Entity Framework Core - Migration

Published
6 min read

1. How Migration Generation Works (The Internals)

Many developers believe Add-Migration compares your code to the database. It does not. It compares your code to the Model Snapshot.

The "Diff" Mechanism

When you run Add-Migration <Name>, EF Core performs a 3-step process:

  1. Build the Current Model: It scans your DbContext, DbSet<T> properties, and OnModelCreating configurations to build an in-memory representation of what your database should look like right now.

  2. Load the Snapshot: It reads the [YourContext]ModelSnapshot.cs file (located in your Migrations folder). This file represents the state of your model when the last migration was generated.

  3. Compare (The Diff): It compares the Current Model vs. Model Snapshot.

    • Difference found? It generates a new Migration file with Up() and Down() methods to bridge that gap.

    • No difference? It returns an empty migration.

Crucial Insight: The database is not touched during generation. You can generate migrations without a network connection to the DB.

The Artifacts

  • YYYYMMDDHHMMSS_MigrationName.cs: Contains the specific changes (Delta).

    • Up(): Instructions to apply changes (e.g., CreateTable).

    • Down(): Instructions to revert changes (e.g., DropTable).

  • [Context]ModelSnapshot.cs: The cumulative "Master Plan." After every migration generation, this file is rewritten to match the Current Model. It is the "Source of Truth" for the next migration.


2. Rollback: The Two Types

There is a critical distinction between reverting the database and removing the migration file.

A. Reverting the Database (The "Down" Direction)

If you have applied a bad migration to the database and want to go back, you use the Update-Database command targeting a previous migration.

  • Logic: EF Core checks the __EFMigrationsHistory table in the DB. If the target migration is older than the current DB state, it executes the Down() method of every migration between the current state and the target.

  • Command:

    • CLI: dotnet ef database update <NameOfPreviousMigration>

    • PMC: Update-Database <NameOfPreviousMigration>

  • The "Zero" Target: To revert all migrations and empty the database:

    • dotnet ef database update 0

B. Removing the Migration File (Cleanup)

If you generated a migration but have not applied it to the database yet (or just reverted it using step A), you can delete the C# file.

  • Command: dotnet ef migrations remove

  • What it does:

    1. Removes the last migration .cs file.

    2. Reverts the ModelSnapshot.cs to the state of the previous migration.

  • Exception: If you try this while the migration is already applied to the DB, EF Core will throw an error: "The migration 'X' has already been applied to the database. Revert it and try again."


3. Corner Cases & Complex Scenarios

Scenario A: Merge Conflicts in ModelSnapshot.cs (Team Environment)

Two developers add a migration on their own branches. When they merge, Git usually handles the migration files fine (since they have different timestamps), but the ModelSnapshot.cs will have a Merge Conflict.

  • The Fix:

    1. Resolve the Git conflict manually (often messy).

    2. Better approach: Accept either version (or the combined version if possible), then force EF to regenerate the correct snapshot.

    3. Run dotnet ef migrations add TemporaryFix.

    4. If the snapshot is correct, this migration will be empty.

    5. Run dotnet ef migrations remove. EF will rebuild the snapshot based on the actual migration history files.

Scenario B: Data Motion (Breaking Changes)

You rename a column from FullName to Name. EF Core sees this as:

  1. DropColumn(FullName) (Data Loss!)

  2. AddColumn(Name) (New empty column)

The Fix (Manual Intervention):

You must edit the generated migration file before applying it.

protected override void Up(MigrationBuilder migrationBuilder)
{
    // EF Generated:
    // migrationBuilder.DropColumn(name: "FullName", table: "Users");
    // migrationBuilder.AddColumn<string>(name: "Name", table: "Users", nullable: true);

    // REPLACE WITH:
    migrationBuilder.RenameColumn(
        name: "FullName",
        table: "Users",
        newName: "Name");
}

Scenario C: Multiple DbContexts

If your project has multiple contexts (e.g., AppDbContext and IdentityDbContext), EF Core doesn't know which one to migrate.

  • Error: "More than one DbContext was found..."

  • Fix: Always specify the context.

    • dotnet ef migrations add NewMig -c AppDbContext

    • dotnet ef database update -c AppDbContext


4. Common Exceptions & Errors

Exception / ErrorCauseSolution
"The migration 'X' has already been applied..."You ran remove-migration but the migration exists in the __EFMigrationsHistory table.Run dotnet ef database update <PreviousMigration> first, then remove.
"Build Failed."Your code doesn't compile. EF cannot build the model if the C# code is broken.Fix compiler errors before generating migrations.
"Unable to create an object of type 'MyContext'..."EF Core cannot find a parameterless constructor or IDesignTimeDbContextFactory.Add a parameterless constructor or implement IDesignTimeDbContextFactory to tell EF how to create the context at design time.
"Pending Model Changes" WarningThe DB does not match the snapshot, even though all migrations are applied.You likely changed the code but forgot to Add-Migration. Generate the new migration.

5. Best Practices

1. Idempotent Scripts for Production

Never run dotnet ef database update in production. It relies on the development SDK being installed and is hard to debug if it fails.

Instead, generate a SQL script that checks which migrations are applied and only applies the missing ones.

  • Command:

    dotnet ef migrations script --idempotent --output "migration.sql"

  • Result: A SQL file wrapping migrations in IF NOT EXISTS blocks. Hand this to your DBA or deployment pipeline.

2. Naming Conventions

Treat migrations like Git Commits.

  • Update1, Fix, Changes

  • AddUserBirthDate, RenameCustomerTable, MakeProductSkuUnique

3. Separate Assembly for Migrations

For Clean Architecture, your DbContext (Domain/Infra layer) should not depend on the specific Migrations (which are implementation details).

Configure your startup to store migrations in a separate project (e.g., MyApp.Data.Migrations).

services.AddDbContext<MyContext>(options =>
    options.UseSqlServer(
        connectionString,
        x => x.MigrationsAssembly("MyApp.Data.Migrations")));

4. Review the Up AND Down Methods

Developers often check the Up() method but ignore Down(). If Down() fails (e.g., tries to drop a table that has foreign key dependencies not handled), you will be stuck in a failed state during a rollback emergency. Always verify Down() logic for complex changes.

Quick Command Reference (CLI/PMC)

Action.NET CLI CommandPMC Command (Visual Studio)
Generatedotnet ef migrations add <Name>Add-Migration <Name>
Apply (All)dotnet ef database updateUpdate-Database
Apply (Specific)dotnet ef database update <Target>Update-Database <Target>
Revert (One Step)dotnet ef database update <Prev>Update-Database <Prev>
Revert (All)dotnet ef database update 0Update-Database 0
Delete Filedotnet ef migrations removeRemove-Migration
Generate SQLdotnet ef migrations script --idempotentScript-Migration -Idempotent

More from this blog

E

EF Core

31 posts