dotnet ef has three groups of commands, migrations, database and
dbcontext, and two options that decide whether any of them work at all. This is every
command a developer uses, run on a real two-project solution against SQL Server LocalDB with EF Core
10.0.13, with what each one printed. Keep it open the next time a migration has to be undone or a
production script has to be generated.
- A class library with the
DbContextand a web project that references it; addMicrosoft.EntityFrameworkCore.Designto the web project too - From the solution folder, run
dotnet ef migrations add Initialwithout options and read the error, then add--projectand--startup-project - Add a second entity and a second migration; run
migrations listandmigrations has-pending-model-changes database update, thendatabase update Initialto roll the second one back, thenmigrations removemigrations script --idempotent -o migrate.sqland open the filedbcontext scaffoldthe database into a third project and compare the generated context with yoursdatabase drop -fwhen you are done
Install the tool, globally or per solution
The tool is a separate package from EF Core itself, and its version can lag behind your packages. This PC has the global tool at 10.0.0 while the projects use EF Core 10.0.13, so running it from a folder outside the solution printed a warning before doing the work:
The Entity Framework tools version '10.0.0' is older than that of the runtime '10.0.13'. Update the tools for the latest features and bug fixes. See https://aka.ms/AAc1fbw for more information.
The cleaner setup pins the tool with the solution: dotnet new tool-manifest in the solution
folder, then dotnet tool install dotnet-ef, which installed 10.0.13 and made
dotnet ef --version print 10.0.13 in that folder while the global tool stayed at 10.0.0.
One surprise on SDK 10.0.402: the manifest was written as dotnet-tools.json in the folder
itself, not under .config as older documentation shows.
Two projects, two options
The target project is where the migration files go; the startup project is the one the tool builds
and runs to learn your model and connection string. With the context in EfRef.Data and
the app in EfRef.Web, running from the solution folder gives this, because a solution
file is not a project:
No project was found. Change the current working directory or use the --project option.
The form that names both projects then failed for a reason that catches most people once:
Your startup project 'EfRef.Web' doesn't reference Microsoft.EntityFrameworkCore.Design. This package is required for the Entity Framework Core Tools to work. Ensure your startup project is correct, install the package, and try again.
dotnet add package marks the Design package as private, so a reference in the class
library does not flow to the web project. Add it to the startup project as well, and the command
goes through:
dotnet ef migrations add Initial --project EfRef.Data --startup-project EfRef.Web --output-dir Migrations
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
Three files appeared in EfRef.Data\Migrations: the migration, its designer file and the
model snapshot. The migration id starts with a UTC timestamp, which is why it does not match your
clock. --output-dir Migrations is the default and can be left out.
add, list and has-pending-model-changes
After a second entity and migrations add AddRooms, migrations list showed both
ids marked (Pending), with no database existing yet and no error. The check that belongs in
a CI pipeline is migrations has-pending-model-changes: it printed "No changes have been
made to the model since the last migration." with exit code 0, and after a property was added to an
entity without a migration it printed "Changes have been made to the model since the last migration.
Add a new migration." with exit code 1. A build step that runs it fails the build when someone forgot
the migration.
update, roll back, remove
database update created the database, took the migrations lock, created
__EFMigrationsHistory, applied both migrations and recorded each with the product version:
Applying migration '20261014043742_Initial'.
A migration that has been applied cannot simply be removed; the tool connects and checks:
The migration '20261014043810_AddRooms' 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.
Rolling back means updating to the migration before it, by name:
Reverting migration '20261014043810_AddRooms'.
DROP TABLE [Rooms];
After that, migrations remove printed "Removing migration", "Reverting the model snapshot."
and "Done.", leaving only the first migration and the snapshot. The second sentence of the refusal
above is the production rule: once a migration has reached a database you do not own, do not remove
it; write a new migration that undoes it.
Scripts for production
migrations script prints the SQL for every migration, each in its own transaction with a
GO after it. With --idempotent every statement is wrapped in a check against the
history table, so the same file can be run on a database at any migration:
IF NOT EXISTS (SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20261014043742_Initial')
A range, migrations script Initial AddRooms, prints only AddRooms: the FROM migration is
the one you already have, and the documentation says 0 stands for "before the first migration".
-o migrate.sql writes the file into the current folder, not the project folder, and prints
only the build lines. The alternative to a script is a bundle: migrations bundle built
efbundle.exe, which applied both migrations to an empty database and, run a second time,
said "No migrations were applied. The database is already up to date." Copy
appsettings.json next to it if the connection string lives there; the tool reminds you.
The migration SQL explainer on this site reads a generated
script and lists what it will create, drop and alter.
dbcontext: info, list, optimize, scaffold
dbcontext info printed the context type, the provider, the database name and the data
source, which is the fastest way to see which connection string the tools are really using.
dbcontext list names every context in the project. dbcontext optimize generated a
compiled model, five files under CompiledModels plus an assembly attribute that registers
it automatically, for large models where startup time matters:
Successfully generated a compiled model, it will be discovered automatically, but you can also call 'options.UseModel(ClinicContextModel.Instance)'. Run this command again when the model is modified.
The reverse direction, an existing database into code, is one command:
dotnet ef dbcontext scaffold "Server=(localdb)\MSSQLLocalDB;Database=Fix4EfRef;Trusted_Connection=True;TrustServerCertificate=True" Microsoft.EntityFrameworkCore.SqlServer --project EfRef.Scaffold -o Models --context-dir Data --context ClinicDbContext --no-onconfiguring
It produced an entity per table and a context with an options constructor and no connection string,
and it skipped the history table. For a quick look at what a CREATE TABLE script becomes
without a database at all, the DDL to EF Core tool does the same in
the browser.
The two errors everyone meets
Pointing the tool at the class library alone, with no startup project that can build the context:
Unable to create a 'DbContext' of type 'ClinicContext'. The exception 'Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[EfRef.Data.ClinicContext]' while attempting to activate 'EfRef.Data.ClinicContext'.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
That one and "No project was found" each have a Fixes post: Unable to create a DbContext of type and No project was found. The third one, a migration applied twice because the folder and the history table disagree, is There is already an object named.
Quick reference
| Command | What it does | Connects to the database |
|---|---|---|
migrations add Name | Diffs the model against the snapshot, writes the migration | No |
migrations list | Lists migrations with their applied state | Yes, unless --no-connect |
migrations has-pending-model-changes | Exit 1 when the model has changes without a migration | No |
migrations remove | Deletes the last migration and reverts the snapshot; refuses if applied | Yes |
migrations script [From] [To] | SQL for a range; --idempotent for any starting state | No |
migrations bundle | Builds an executable that applies pending migrations | No (the bundle does) |
database update [Name] | Applies up to, or rolls back to, a migration | Yes |
database drop -f | Drops the database without asking | Yes |
dbcontext info / list | Shows the context, provider and data source | No |
dbcontext optimize | Generates a compiled model | No |
dbcontext scaffold | Generates entities and a context from a database | Yes |
Finally, database drop -f printed "Dropping database 'Fix4EfRef' on server
'(localdb)\MSSQLLocalDB'." and "Successfully dropped database 'Fix4EfRef'.", and the test database was
gone. Not run here, so not described: migrations remove --force, database update 0,
and the EF Core 11 options the documentation already lists.
Frequently asked
- What is the difference between --project and --startup-project in dotnet ef?
- The project, or target project, is where the tool writes and removes migration files; by default it is the project in the current folder. The startup project is the one the tool builds and runs to read your model and connection string. When the DbContext lives in a class library and a web app references it, pass --project for the library and --startup-project for the app, and make sure the app references Microsoft.EntityFrameworkCore.Design, because that package does not flow from the library.
- How do I roll back an EF Core migration?
- Run dotnet ef database update with the name of the migration you want to be the last applied one, for example database update Initial; the tool reverts the later migrations and deletes their rows from __EFMigrationsHistory. Then dotnet ef migrations remove deletes the migration file and reverts the snapshot. If the migration has already been applied to a database you do not control, write a new migration that undoes it instead of removing it.
- How do I apply EF Core migrations in production?
- Generate the SQL once with dotnet ef migrations script --idempotent -o migrate.sql and run it from the deployment pipeline after a backup; the idempotent script checks __EFMigrationsHistory before every statement, so it is safe on a database at any migration. The alternative is dotnet ef migrations bundle, which builds an executable that applies pending migrations and reports when there is nothing to do.
How migrations behave in production, with the add-column-backfill-drop sequence run on both engines, is Part 11 of Database Design for Developers, and the interview answers that lean on these commands are in EF Core interview questions.