SAPFIAI uses Entity Framework Core for database access and migrations. This guide covers the complete workflow for managing your database schema.
Overview
Migrations are stored in src/Infrastructure/Data/Migrations/ and track changes to your database schema over time. Each migration is a C# class that describes how to apply and revert schema changes.
Prerequisites
Ensure you have the EF Core tools installed:
dotnet tool install --global dotnet-ef
Or update to the latest version:
dotnet tool update --global dotnet-ef
Creating Migrations
After modifying entities in the Domain layer or configurations in Infrastructure, create a migration to capture the changes.
Basic Syntax
dotnet ef migrations add "MigrationName" \
--project src/Infrastructure \
--startup-project src/Web \
--output-dir Data/Migrations
Parameters Explained
The project containing your DbContext (Infrastructure)
The project with your connection string configuration (Web)
Where to generate migration files (Data/Migrations)
Migration Naming Conventions
Use descriptive names that indicate what changed:
# Good examples
dotnet ef migrations add "AddUserEmailIndex" --project src/Infrastructure --startup-project src/Web --output-dir Data/Migrations
dotnet ef migrations add "UpdatePermissionModuleLength" --project src/Infrastructure --startup-project src/Web --output-dir Data/Migrations
dotnet ef migrations add "CreateAuditLogTable" --project src/Infrastructure --startup-project src/Web --output-dir Data/Migrations
# Avoid generic names
dotnet ef migrations add "Update1" --project src/Infrastructure --startup-project src/Web --output-dir Data/Migrations
Migration Files
Each migration creates two files in src/Infrastructure/Data/Migrations/:
Migration Class
20260215221946_InitialCreate.cs:
using Microsoft . EntityFrameworkCore . Migrations ;
namespace SAPFIAI . Infrastructure . Data . Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up ( MigrationBuilder migrationBuilder )
{
// Apply changes
migrationBuilder . CreateTable (
name : "Permissions" ,
columns : table => new
{
Id = table . Column < int >( nullable : false )
. Annotation ( "SqlServer:Identity" , "1, 1" ),
Name = table . Column < string >( maxLength : 100 , nullable : false ),
Module = table . Column < string >( maxLength : 50 , nullable : false )
},
constraints : table =>
{
table . PrimaryKey ( "PK_Permissions" , x => x . Id );
});
}
protected override void Down ( MigrationBuilder migrationBuilder )
{
// Revert changes
migrationBuilder . DropTable ( name : "Permissions" );
}
}
}
Designer File
20260215221946_InitialCreate.Designer.cs contains metadata about the model state.
Never manually edit Designer files. Always use EF Core commands to modify migrations.
Applying Migrations
Apply pending migrations to update your database schema.
Apply All Pending Migrations
dotnet ef database update \
--project src/Infrastructure \
--startup-project src/Web
This applies all migrations that haven’t been applied yet.
Apply Specific Migration
Rollback or roll forward to a specific migration:
# Rollback to specific migration
dotnet ef database update MigrationName \
--project src/Infrastructure \
--startup-project src/Web
# Rollback all migrations
dotnet ef database update 0 \
--project src/Infrastructure \
--startup-project src/Web
Development Environment
In development, migrations can be applied automatically on startup. The application checks the environment in Program.cs:56:
if ( app . Environment . IsDevelopment () && ! skipDatabaseInitialization )
{
app . UseDeveloperExceptionPage ();
await app . InitialiseDatabaseAsync ();
}
Set the SKIP_DB_INIT environment variable to true to disable automatic migrations during development.
Generating SQL Scripts
For production deployments, generate SQL scripts instead of applying migrations directly.
Generate Complete Script
dotnet ef migrations script \
--project src/Infrastructure \
--startup-project src/Web \
--output migration.sql
This creates a SQL script with all migrations.
Generate Incremental Script
Generate a script for migrations between two points:
# From specific migration to latest
dotnet ef migrations script FromMigration \
--project src/Infrastructure \
--startup-project src/Web \
--output incremental.sql
# Between two migrations
dotnet ef migrations script FromMigration ToMigration \
--project src/Infrastructure \
--startup-project src/Web \
--output incremental.sql
Idempotent Scripts
Generate scripts that can be run multiple times safely:
dotnet ef migrations script \
--project src/Infrastructure \
--startup-project src/Web \
--output migration.sql \
--idempotent
Idempotent scripts include checks to skip migrations that have already been applied.
Removing Migrations
Remove the last migration if it hasn’t been applied to any database yet.
dotnet ef migrations remove \
--project src/Infrastructure \
--startup-project src/Web
Never remove a migration that has been:
Applied to any database (dev, staging, or production)
Committed to source control and shared with the team
Deployed to any environment
Instead, create a new migration to revert the changes.
Migration Workflow
Follow this workflow when making database changes:
Modify Domain Entities
Update entity classes in src/Domain/Entities/ or add new ones. public class Permission
{
public int Id { get ; set ; }
public string Name { get ; set ; } = string . Empty ;
// Add new property
public string Category { get ; set ; } = "General" ;
}
Update Entity Configuration (Optional)
If needed, add or modify entity configurations in src/Infrastructure/Data/Configurations/. public class PermissionConfiguration : IEntityTypeConfiguration < Permission >
{
public void Configure ( EntityTypeBuilder < Permission > builder )
{
builder . Property ( p => p . Category )
. HasMaxLength ( 50 )
. IsRequired ();
}
}
Create Migration
dotnet ef migrations add "AddPermissionCategory" \
--project src/Infrastructure \
--startup-project src/Web \
--output-dir Data/Migrations
Review Generated Migration
Open the migration file and verify the changes are correct. Make manual adjustments if needed.
Test Migration
# Apply migration
dotnet ef database update --project src/Infrastructure --startup-project src/Web
# Test your application
dotnet run --project src/Web
# If issues found, rollback and fix
dotnet ef database update PreviousMigration --project src/Infrastructure --startup-project src/Web
Commit Changes
git add src/Domain/ src/Infrastructure/
git commit -m "Add Category field to Permission entity"
Configuration
Connection Strings
Connection strings are configured in appsettings.json:
{
"ConnectionStrings" : {
"DefaultConnection" : "Server=(localdb) \\ mssqllocaldb;Database=SAPFIAIDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
For production, override in appsettings.Production.json or use environment variables:
export ConnectionStrings__DefaultConnection = "Server=prod-server;Database=SAPFIAIDb;User Id=app_user;Password=***"
Multiple Databases
If using different databases per environment:
# Development (SQL Server LocalDB)
dotnet ef database update --project src/Infrastructure --startup-project src/Web
# Production (specify connection at runtime)
dotnet ef database update \
--project src/Infrastructure \
--startup-project src/Web \
--connection "Server=prod;Database=SAPFIAIDb;User Id=sa;Password=***"
Common Scenarios
Create entity in src/Domain/Entities/NewEntity.cs
Add DbSet<NewEntity> to ApplicationDbContext
Create migration: dotnet ef migrations add "CreateNewEntityTable" --project src/Infrastructure --startup-project src/Web --output-dir Data/Migrations
Apply: dotnet ef database update --project src/Infrastructure --startup-project src/Web
Add property to entity class
Create migration: dotnet ef migrations add "AddColumnToEntity" --project src/Infrastructure --startup-project src/Web --output-dir Data/Migrations
Apply: dotnet ef database update --project src/Infrastructure --startup-project src/Web
Rename property in entity
Create migration: dotnet ef migrations add "RenameColumn" --project src/Infrastructure --startup-project src/Web --output-dir Data/Migrations
Edit migration to use RenameColumn instead of DropColumn + AddColumn:
migrationBuilder . RenameColumn (
name : "OldName" ,
table : "TableName" ,
newName : "NewName" );
Apply: dotnet ef database update --project src/Infrastructure --startup-project src/Web
Migrations can include data updates: protected override void Up ( MigrationBuilder migrationBuilder )
{
// Schema changes
migrationBuilder . AddColumn < string >(
name : "Category" ,
table : "Permissions" ,
nullable : false ,
defaultValue : "General" );
// Data migration
migrationBuilder . Sql (
"UPDATE Permissions SET Category = 'Security' WHERE Module = 'Auth'" );
}
Troubleshooting
”No migrations configuration type was found”
Ensure you’re specifying both --project and --startup-project:
dotnet ef migrations add "Migration" \
--project src/Infrastructure \
--startup-project src/Web
“A connection was not found”
Verify your connection string in appsettings.json and ensure SQL Server is running:
# Check connection string
cat src/Web/appsettings.json | grep ConnectionStrings
# Test connection (if using LocalDB)
sqllocaldb info
“The migration has already been applied”
Check applied migrations:
dotnet ef migrations list --project src/Infrastructure --startup-project src/Web
Pending Model Changes
Check if there are pending model changes:
dotnet ef migrations has-pending-model-changes \
--project src/Infrastructure \
--startup-project src/Web
Production Deployment
For production deployments:
Generate SQL Script
dotnet ef migrations script \
--project src/Infrastructure \
--startup-project src/Web \
--output migration_script.sql \
--idempotent
Review Script
Manually review the generated SQL for any potential issues or data loss.
Test on Staging
Apply the script to a staging environment first: sqlcmd -S staging-server -d SAPFIAIDb -i migration_script.sql
Backup Production
Always backup the production database before applying migrations.
Apply to Production
sqlcmd -S prod-server -d SAPFIAIDb -i migration_script.sql
Never run dotnet ef database update directly against production. Always use SQL scripts for production deployments.
Next Steps
Creating Use Cases Learn how to implement CQRS commands and queries
Testing Test your database interactions