Migrations in Entity Framework Core have become very powerful and easy to use. This is by no means a complete guide! Rather, this acts as a Quick Reference Guide for myself. For official documentation on migrations in EF Core, visit https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/

Assumptions

  • You already have a project using Entity Framework Core
  • You have a basic understanding of how migrations work (if not, get that from the official docs)
  • The “Command Line” referenced throughout this guide is the Visual Studio Developer Command Prompt (Tools -> Command Line -> Developer Command Prompt)

Install EF Core Tools for .Net CLI

First you need to install the Entity Framework Core Tools for the .NET Command-Line Interface (CLI). It is installed globally and not per-project, so it may already be installed if you’ve used migrations on another project. If it’s not installed, running any of the dotnet ef commands on this page will result in the following error: Could not execute because the specified command or file was not found.

To install the tool, open the command prompt in Visual Studio using menu path Tools -> Command Line -> Developer Command Prompt, then enter the following command:

dotnet tool install --global dotnet-ef

If you get the error message Tool 'dotnet-ef' is already installed. and you’re unsure of which version is installed, attempt to update to the latest version:

dotnet tool update --global dotnet-ef

Add a Migration

After making changes to a model, you can bundle those changes into a migration. Whether it’s yet another set of changes to an existing model or you’ve just created a new model, the command is the same:

dotnet ef migrations add <name> // <-- Give it a name (e.g. "InitialCreate")

Migrations files are automatically prefixed with a timestamp, so regardless of the names you give them, they will be sorted (and executed) in chronological order. Migrations will appear by default in a /Migrations directory.

Remove a Migration

To remove a migration, use the remove command. This will remove the most recent migration, so you don’t need to include the migration’s name.

dotnet ef migrations remove

Note: If the migration has already been applied to the database, you need to revert it first (see below). If your changes have been deployed to multiple databases, it might be better to adjust your model and create a new migration instead of reverting & removing an existing one.

Apply Migrations to Database

To update the database with changes saved in all migrations:

dotnet ef database update

To update the database with changes saved in all migrations up to and including a specific one:

dotnet ef database update migration_before_last

Revert Migrations in Database (Rollback)

To revert the database back to a specific migration (e.g. latest migration is bad and needs to be reverted & removed):

dotnet ef database update last_good_migration // Note: Same command as above

To revert the database back to its original state before any migrations were applied, use 0 as the migration name. Note that this will delete anything in the database that was created by any migrations, usually leaving an empty database! This command will also create a new, empty database if one doesn’t already exist.

dotnet ef database update 0

Generate SQL Migration Script (for Production Servers)

When applying migrations to a production database server, it seems like the general consensus (one example) is that one should not run the EF migration tools directly against a production database server. Instead, it is better to generate change scripts, test them on a staging server, then apply them to the production server.

The migration tool can generate the database change scripts for your migrations. In fact, the generated scripts also reference and update the table __EFMigrationsHistory, preventing migrations from being applied to the database multiple times. Just generate the script file, review it (of course), and it should be safe to apply to staging and then production.

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

Scripting Specific Migrations (usually not required)

Even though scripting all migrations is safe (because the database will only apply the ones it’s missing), you may still want to generate scripts to apply specific migrations. This is where the <from> and <to> parameters come in handy.

Specify only the <from> parameter to script migrations to bring the database from the <from> migration (already applied to db & not included in script) to the latest migration:

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

Specify both the <from> and <to> parameters to script migrations to bring the database from one migration to another migration, e.g. from migration2 (already applied to db & not included in script) to migration5:

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

Specify <from> as 0 and another migration as <to> to script migrations from the initial database state (empty) to another migration, e.g. going from an empty database to migration3:

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

Note: Specifying <from> as 0 without a <to> parameter has the exact same effect as running the command with no <from> parameter, as it scripts all migrations. So I’m not including it as an example.

Migrations in Model in Separate Class Library

It is pretty common to create your model as a separate Class Library project within the same solution as your front-end Web/API/Console projects. In this scenario, simply calling the dotnet ef migrations command won’t work. If you call it inside the model class library project, it won’t be aware of the connection strings, as those are controlled within the Web/API/Console projects. And you can’t call it from the Web/API/Console projects which reference the class library as the tool expects the model to be in the project from which it is called.

This is where the following parameters come in handy:

  • --project: The project containing the model in which the migrations will be applied
  • --startup-project: The project which contains the connection strings and references the model project

If you’re already in the root of the solution directory, providing the names of the projects as parameters is sufficient:

dotnet ef migrations add initialcreate --project MyProjectModel --startup-project MyProjectWeb

If for some reason the example above doesn’t work, you can point specifically to the actual project files:

dotnet ef migrations add initialcreate --project .\MyProjectModel\MyProjectModel.csproj --startup-project .\MyProjectWeb\MyProjectWeb.csproj

Front-Loaded Parameters (for Convenience)

When working with these commands, it’s pretty common to either copy/paste them or hit the up arrow in the command line to bring up the latest command. But in both cases, the cursor is at the end of the command, so you have to use the left arrow (or Ctrl+LeftArrow) to skip to the beginning of the line so you can edit the actual commands. To get around this, we can also front-load the two parameters that don’t change, leaving the stuff we’ll manually update at the end where the cursor is:

dotnet ef --project MyProjectModel --startup-project MyProjectWebmigrations database update

For more information about these parameters, check out the official docs.

Batch Files (for More Convenience)

It’s a piece of cake to set up batch files that call the migration commands with the proper parameters.

ma.bat (migrations add <name>):

dotnet ef --project MyProjectModel --startup-project MyProjectWeb migrations add %1

mr.bat (migrations remove):

dotnet ef --project MyProjectModel --startup-project MyProjectWeb migrations remove

du.bat (database update <optional-name-or-0>):

dotnet ef --project MyProjectModel --startup-project MyProjectWeb database update %1

ml.bat (migrations list):

dotnet ef --project MyProjectModel --startup-project MyProjectWeb migrations list

Migration on Publish in Visual Studio

The Visual Studio Publish feature includes a setting that lets you apply migrations to the remote detatabase, including the option to specify the connection string to be used when applying the migrations. However, I haven’t personally had any luck getting this feature to work in my environments. Your mileage may vary.

Enabling this setting does generate the SQL script required to apply the migrations to a remote SQL server, which can be handy if you are lazy and don’t want to manually execute the command to generate the scripts.

The tool runs this command:

dotnet ef migrations script --idempotent --output "<your-project-path>\obj\Release\netcoreapp3.1\PubTmp\EFSQLScripts\<your-project-namespace>.ApplicationDbContext.sql" --context <your-project-namespace>.ApplicationDbContext 

Automatic Migration via Detailed Database Error Page (Dirty/Lazy Mode)

In non-production environments, there is a detailed database error page that will give you an “Apply Migrations” button in the event that you have migrations in the project that are not yet applied to the database. While it’s better to apply the migrations the normal way (dotnet ef database update), the button on this error message does do the job.

The “Apply Migrations” button will only appear under the following conditions:

  • You have added migrations to the project that have not been applied to the database
  • You have added app.UseDatabaseErrorPage() to Configure() in Startup.cs (requires NuGet package below)
  • The project has the following NuGet package installed: Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore v3.1.1
  • The application is not running in Production mode (based on the environment variable ASPNETCORE_ENVIRONMENT)

Adding this detailed database error page is covered in another guide: Create ASP.Net Core 3.1 MVC Project With Entity Framework

Rename a Migration (Unusual)

If for some reason you must rename a migration, e.g. you deviated from a naming scheme and it bothers you enough to risk breaking everything to fix it, this section is for you.

Locations where you’ll need to change the migration name:

  • File names of migration files:
    • yyyymmddhhmmss_OldName.cs
    • yyyymmddhhmmss_OldName.Designer.cs
  • Class names in the same files above:
    • Partial Class OldName
  • Migration annotations in the yyyymmddhhmmss.Designer.cs file:
    • [Migration("yyyymmddhhmmss_OldName")]
  • Databases where migration was applied (dev, staging, prod, etc.):
    • Table “__EFMigrationsHistory”