In this article, we will show how to revert a migration in EF Core. Entity Framework Core is an excellent tool for managing data structure by using familiar object types. If you need a refresher on the basics of EF Core, be sure to check out our full Entity Framework Core series!
Now EF Core is great at helping us create “Code First” data models, but when it comes to updating those models, migrations can be less than straightforward.
One of the least intuitive things we can do in EF Core is to roll back the changes in a migration after we’ve already updated the database.
Create a New Project with EF Core
Let’s set ourselves up by creating a new .NET project using the dotnet CLI:
dotnet new console --name RevertMigration
We will add the Entity Framework Core package from NuGet. EF Core supports several database types, but for simplicity, we’ll use SQLite:
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
Add DB Context and Entity Models>
Now that we have EF Core installed, let’s create some entity models and add them to a data context. First, we’ll create an Airplane
object:
public class Airplane { public int Id { get; set; } public string? TailNumber { get; set; } public int? NumberOfEngines { get; set; } public double? MaxAirSpeed { get; set; } public bool? RunsOnJetFuel { get; set; } public int? HangarId { get; set; } public Hangar? Hangar { get; set; } }
Next, let’s create a Hangar
to park our planes:
public class Hangar { public int Id { get; set; } public string? HangarNumber { get; set; } public bool HasDoors { get; set; } public List Airplanes { get; } = new(); }
And finally, let’s create our DBContext
:
public class AirportDbContext : DbContext { public DbSet Airplanes { get; set; } public DbSet Hangars { get; set; } public string DbPath { get; } public AirportDbContext() { var folder = Environment.SpecialFolder.LocalApplicationData; var path = Environment.GetFolderPath(folder); DbPath = Path.Join(path, "airport.db"); } protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseSqlite($"Data Source={DbPath}"); }
We’ll run the following commands to install EF Core Tools and scaffold our data context:
dotnet tool install --global dotnet-ef dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add Init dotnet ef database update
Great, now we have a database! Let’s add a new hangar and an airplane, saving them to our database as we go:
using var db = new AirportDbContext(); Console.WriteLine("Inserting a new hangar"); db.Add(new Hangar { HangarNumber = "HANGAR_01", HasDoors = true }); db.SaveChanges(); var hangar = db.Hangars .OrderBy(h => h.Id) .Last(); Console.WriteLine($"Just created hangar: {hangar.HangarNumber}"); Console.WriteLine($"Inserting a new airplane"); hangar.Airplanes.Add(new Airplane { TailNumber = "ABC123", MaxAirSpeed = 300, RunsOnJetFuel = true }); db.SaveChanges(); var airplane = db.Airplanes .OrderBy(a => a.Id) .Last(); Console.WriteLine($"Just created airplane: {airplane.TailNumber} in hangar: {hangar.HangarNumber}");
We’ve included queries to check that our entities are saved to the database, and we print the output:
InsertingĀ aĀ newĀ hangar Just created hangar: HANGAR_01 Inserting a new airplane Just created airplane: ABC123 in hangar: HANGAR_01
Add a New Migration
Database schemas often change as our project evolves. Let’s make some changes to the entity model and scaffold a new migration to update the database. We’ll add a new property HasDoors
to our Hangar class:
public class Hangar { ... public bool HasDoors { get; set; } ... }
Then we’ll use the dotnet CLI to apply the new migration to the database:
dotnet ef migrations add Hangar_HasDoors dotnet ef database update
Our Migrations folder now has a second migration. We’ve also applied those changes to the database schema.
Make Changes to an Existing Migration
Now, for the fun part. Let’s say we find something wrong with the migration we just created. We can make another change and create a third migration, but that’s going to make our migration history messy. Instead, it will be better to remove it and create a new, correct one. If we hadn’t already updated the database schema, we could just remove it:Ā
dotnet ef migrations remove
But, no! We’re not allowed to remove this migration because it has already been applied to the schema:
The migration '20231209231131_Hangar_HasDoors' has already been applied to the database. Revert it and try again. If the migration has been applied to other databases, consider reverting its changes using a new migration instead.
Revert a Migration Already Applied
The good news is, there’s an easy way to deal with this. EF Core allows us to revert one or more migrations by running the database update
command targeting an earlier migration:
dotnet ef database update Init
The key here is specifying the target migration “Init” so that EF will roll back any later migrations:
Reverting migration '20231209231131_Hangar_HasDoors'. Done.
Then we will remove the last migration:
dotnet ef migrations remove
Removing migration '20231209231131_Hangar_HasDoors'. Reverting the model snapshot. Done.
It’s also possible to revert to an even earlier migration and then runĀ dotnet ef migrations remove
multiple times to remove more than one migration.
That’s it! Once we’ve removed the faulty migration, we can make changes to our model and add a new migration to replace it.
Conclusion
In this article, we learned how to revert a migration that has already been applied to the database. We must first callĀ dotnet ef database update targeting a previous migration. Once we’ve reverted the migration, we can safely remove it by callingĀ dotnet ef migrations remove.