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.

Do it yourself
  1. A class library with the DbContext and a web project that references it; add Microsoft.EntityFrameworkCore.Design to the web project too
  2. From the solution folder, run dotnet ef migrations add Initial without options and read the error, then add --project and --startup-project
  3. Add a second entity and a second migration; run migrations list and migrations has-pending-model-changes
  4. database update, then database update Initial to roll the second one back, then migrations remove
  5. migrations script --idempotent -o migrate.sql and open the file
  6. dbcontext scaffold the database into a third project and compare the generated context with yours
  7. database drop -f when 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

CommandWhat it doesConnects to the database
migrations add NameDiffs the model against the snapshot, writes the migrationNo
migrations listLists migrations with their applied stateYes, unless --no-connect
migrations has-pending-model-changesExit 1 when the model has changes without a migrationNo
migrations removeDeletes the last migration and reverts the snapshot; refuses if appliedYes
migrations script [From] [To]SQL for a range; --idempotent for any starting stateNo
migrations bundleBuilds an executable that applies pending migrationsNo (the bundle does)
database update [Name]Applies up to, or rolls back to, a migrationYes
database drop -fDrops the database without askingYes
dbcontext info / listShows the context, provider and data sourceNo
dbcontext optimizeGenerates a compiled modelNo
dbcontext scaffoldGenerates entities and a context from a databaseYes

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.