Entity Framework Core - Migration
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:
Build the Current Model: It scans your
DbContext,DbSet<T>properties, andOnModelCreatingconfigurations to build an in-memory representation of what your database should look like right now.Load the Snapshot: It reads the
[YourContext]ModelSnapshot.csfile (located in your Migrations folder). This file represents the state of your model when the last migration was generated.Compare (The Diff): It compares the Current Model vs. Model Snapshot.
Difference found? It generates a new Migration file with
Up()andDown()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
__EFMigrationsHistorytable in the DB. If the target migration is older than the current DB state, it executes theDown()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 removeWhat it does:
Removes the last migration
.csfile.Reverts the
ModelSnapshot.csto 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:
Resolve the Git conflict manually (often messy).
Better approach: Accept either version (or the combined version if possible), then force EF to regenerate the correct snapshot.
Run
dotnet ef migrations add TemporaryFix.If the snapshot is correct, this migration will be empty.
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:
DropColumn(FullName)(Data Loss!)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 AppDbContextdotnet ef database update -c AppDbContext
4. Common Exceptions & Errors
| Exception / Error | Cause | Solution |
| "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" Warning | The 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 EXISTSblocks. 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 Command | PMC Command (Visual Studio) |
| Generate | dotnet ef migrations add <Name> | Add-Migration <Name> |
| Apply (All) | dotnet ef database update | Update-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 0 | Update-Database 0 |
| Delete File | dotnet ef migrations remove | Remove-Migration |
| Generate SQL | dotnet ef migrations script --idempotent | Script-Migration -Idempotent |

