Overview
Entity Framework Core migrations provide a way to incrementally update the database schema to keep it in sync with the application’s data model while preserving existing data.
The Tournament Management App uses migrations for two separate contexts:
DataContext : Tournament domain entities
IdentityDataContext : ASP.NET Core Identity tables
Migration Workflow
The standard workflow for working with migrations involves these steps:
Install EF Core Tools
Install the dotnet-ef global tool: dotnet tool install --global dotnet-ef --version 8
Make Model Changes
Modify entity classes in the Torneo.App.Dominio project or update the DataContext/IdentityDataContext configuration.
Create Migration
Generate a new migration file that captures the model changes: # For DataContext
dotnet ef migrations add MigrationName \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
# For IdentityDataContext
dotnet ef migrations add MigrationName \
--project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Frontend.Areas.Identity.Data.IdentityDataContext
Review Migration
Examine the generated migration file in the Migrations folder to ensure it correctly represents your changes.
Apply Migration
Update the database schema: # For DataContext
dotnet ef database update \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
# For IdentityDataContext
dotnet ef database update \
--project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Frontend.Areas.Identity.Data.IdentityDataContext
Docker Build Migrations
The application’s Dockerfile includes automated migration creation and execution during the build process. This ensures a fresh database schema is always available in the container.
Build Process
# Create and apply DataContext migrations
dotnet ef migrations add InitialCreate \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext \
--no-build --configuration Release
dotnet ef database update \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
# Create and apply IdentityDataContext migrations
dotnet ef migrations add CreateIdentitySchema \
--project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Frontend.Areas.Identity.Data.IdentityDataContext \
--no-build --configuration Release
dotnet ef database update \
--project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Frontend.Areas.Identity.Data.IdentityDataContext
The Docker build process creates two migrations:
InitialCreate for the DataContext (tournament data)
CreateIdentitySchema for the IdentityDataContext (user authentication)
Migration Commands Reference
Creating Migrations
DataContext Migration
Create DataContext Migration
dotnet ef migrations add MigrationName \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
IdentityDataContext Migration
Create IdentityDataContext Migration
dotnet ef migrations add MigrationName \
--project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Frontend.Areas.Identity.Data.IdentityDataContext
Applying Migrations
Update to Latest Migration
dotnet ef database update \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
Update IdentityDataContext
dotnet ef database update \
--project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Frontend.Areas.Identity.Data.IdentityDataContext
Update to Specific Migration
Target Specific Migration
dotnet ef database update MigrationName \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
Listing Migrations
List DataContext Migrations
dotnet ef migrations list \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
Removing Migrations
Only remove a migration if it hasn’t been applied to any database yet. If the migration has been applied, you must roll it back first.
dotnet ef migrations remove \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
Generating SQL Scripts
Generate SQL scripts without applying them to the database:
dotnet ef migrations script \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext \
--output migration.sql
Rollback Procedures
To rollback the database to a previous migration:
List Available Migrations
View all migrations to identify the target migration: dotnet ef migrations list \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
Update to Target Migration
Rollback to a specific migration: dotnet ef database update PreviousMigrationName \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
Remove Unwanted Migrations
After rolling back, remove the migration files: dotnet ef migrations remove \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
Rollback to Empty Database
To completely reset the database:
dotnet ef database update 0 \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext
Using dotnet ef database update 0 will remove all tables from the database. This operation is destructive and will result in data loss.
Common Issues and Solutions
Issue: “No executable found matching command dotnet-ef”
Solution : Install the EF Core tools globally:
dotnet tool install --global dotnet-ef --version 8
Verify installation:
Issue: “Build failed”
Solution : Build the solution before creating migrations:
dotnet build Torneo.App.sln -c Release
Then add the --no-build flag to the migration command:
dotnet ef migrations add MigrationName \
--project Torneo.App.Persistencia/Torneo.App.Persistencia.csproj \
--startup-project Torneo.App.Frontend/Torneo.App.Frontend.csproj \
--context Torneo.App.Persistencia.DataContext \
--no-build --configuration Release
Issue: “Your startup project doesn’t reference Microsoft.EntityFrameworkCore.Design”
Solution : Ensure the startup project (Torneo.App.Frontend) has the following package reference:
Torneo.App.Frontend.csproj
< PackageReference Include = "Microsoft.EntityFrameworkCore.Design" Version = "8.0.0" >
< IncludeAssets > runtime; build; native; contentfiles; analyzers; buildtransitive </ IncludeAssets >
< PrivateAssets > all </ PrivateAssets >
</ PackageReference >
Issue: “Unable to create an object of type ‘DataContext’”
Solution : Ensure the DATABASE_CONNECTION_STRING environment variable is set:
export DATABASE_CONNECTION_STRING = "Data Source=/app/Torneo.db"
Or update the OnConfiguring method in DataContext.cs to use a hardcoded connection string for development.
Issue: Foreign Key Constraint Violations
Solution : The application uses DeleteBehavior.Restrict for all relationships. When deleting entities, you must:
First delete or update all dependent entities
Then delete the parent entity
Example:
Delete Team with Dependencies
// First remove players from the team
foreach ( var jugador in equipo . Jugadores )
{
context . Jugadores . Remove ( jugador );
}
// Then remove the team
context . Equipos . Remove ( equipo );
context . SaveChanges ();
Issue: Migration Conflicts in Docker Build
Solution : If migrations already exist, the Docker build will fail. Either:
Remove existing migrations before building:
rm -rf Torneo.App.Persistencia/Migrations
rm -rf Torneo.App.Frontend/Migrations
Modify the Dockerfile to check for existing migrations before creating new ones
Migration Files Structure
Migration files are generated in the following locations:
DataContext migrations : Torneo.App.Persistencia/Migrations/
IdentityDataContext migrations : Torneo.App.Frontend/Migrations/
Each migration consists of three files:
[Timestamp]_[MigrationName].cs - Contains Up() and Down() methods
[Timestamp]_[MigrationName].Designer.cs - Metadata for the migration
[ContextName]ModelSnapshot.cs - Current state of the entire model
Example Migration Structure
public partial class InitialCreate : Migration
{
protected override void Up ( MigrationBuilder migrationBuilder )
{
migrationBuilder . CreateTable (
name : "Municipios" ,
columns : table => new
{
Id = table . Column < int >( nullable : false )
. Annotation ( "Sqlite:Autoincrement" , true ),
Nombre = table . Column < string >( maxLength : 50 , nullable : false )
},
constraints : table =>
{
table . PrimaryKey ( "PK_Municipios" , x => x . Id );
});
}
protected override void Down ( MigrationBuilder migrationBuilder )
{
migrationBuilder . DropTable ( name : "Municipios" );
}
}
Environment Variables
The following environment variables affect migration behavior:
DATABASE_CONNECTION_STRING
string
default: "Data Source=/app/Torneo.db"
SQLite connection string. Override to use a different database location or SQL Server.
SQL Server SA password. Only required if using SQL Server instead of SQLite.
Best Practices
Always review migrations before applying them to production databases
Test migrations on a development database first
Back up your database before applying migrations in production
Use descriptive migration names that indicate what changed (e.g., AddPhoneNumberToDirector, CreatePartidoTable)
Never modify applied migrations - create a new migration instead
Keep migrations small - one logical change per migration
Generate SQL scripts for production deployments instead of running migrations directly
Data Contexts Learn about DataContext and IdentityDataContext
Domain Entities Explore the domain model entities