Running Migrations
Migrations define schema changes, but they must be applied/executed to actually modify the database. In PearDrop, migrations are applied using the PearDrop CLI which automatically discovers and runs migrations for all DbContexts.
Overview: When Migrations Are Applied
| Environment | How | When |
|---|---|---|
| Local Development | CLI: peardrop migrate | Manually, after generating migrations |
| Staging/Production | CI/CD Pipeline: peardrop migrate | During deployment (automated) |
| Program Startup | Programmatic: await serviceProvider.ApplyMyModuleMigrationsAsync() | Application initialization (optional) |
Applying Migrations with PearDrop CLI
Basic Migration Runner
The PearDrop CLI provides a unified command that discovers all DbContexts (write and read) and applies pending migrations:
# Run all pending migrations
dotnet tool run peardrop migrate
# Or if CLI is installed globally
peardrop migrate
What it does:
- Discovers all
DbContextclasses in your project - Reads connection strings from
appsettings.json - Applies pending migrations to the database
- Works for both write and read contexts automatically
Expected output:
Applying migrations...
Apply migration CreateIdentitySchema...
Apply migration AddEquipmentAggregate...
Apply migration AddEquipmentProjection...
Done.
With Database Seeding
To apply migrations and run seed data:
peardrop migrate --seed
This executes seeding logic defined in your DbContext.OnModelCreating() or custom seed classes.
Connection String Resolution
The peardrop migrate command automatically finds connection strings in this order:
-
Module-specific strings (for framework modules):
ConnectionStrings:PearDrop-AuthConnectionStrings:PearDrop-FilesConnectionStrings:PearDrop-Multitenancy
-
Default application string:
ConnectionStrings:PearDrop
-
Deprecated fallback:
PearDrop:modules:core:PrimaryConnectionString
Example appsettings.json:
{
"ConnectionStrings": {
"PearDrop": "Server=localhost,1440;Database=MyApp;User Id=sa;Password=...;TrustServerCertificate=True;",
"PearDrop-Auth": "Server=localhost,1440;Database=MyApp;User Id=sa;Password=...;TrustServerCertificate=True;"
}
}
Point all connection strings to the same database unless you need physical separation for specific modules.
Programmatic Migration Application
For scenarios where you need migrations to run automatically on app startup, use the module's migration helper method:
// Program.cs
var app = builder.Build();
// Apply migrations programmatically before running the app
await app.Services.ApplyMyModuleMigrationsAsync();
app.Run();
When to use programmatic migrations:
- Development environment for convenience
- Docker container startup (ensures DB is ready)
- Testing environments
When NOT to use:
- Production deployments (use CI/CD pipeline with
peardrop migrate) - Scenarios requiring migration rollback control
- When migrations need manual approval
Example: Auth Module
Framework modules provide extension methods:
await app.Services.ApplyPearDropAuthenticationMigrationsAsync();
Example: Custom Module
Create your own extension method:
public static class ServiceProviderExtensions
{
public static async Task<IServiceProvider> ApplyMyModuleMigrationsAsync(
this IServiceProvider serviceProvider)
{
using var scope = serviceProvider.CreateScope();
// Apply write model migrations
var writeDbContextFactory = scope.ServiceProvider
.GetRequiredService<IDbContextFactory<MyModuleWriteDbContext>>();
var writeCtx = await writeDbContextFactory.CreateDbContextAsync();
await writeCtx.Database.MigrateAsync();
// Apply read model migrations
var readDbContextFactory = scope.ServiceProvider
.GetRequiredService<IDbContextFactory<MyModuleReadDbContext>>();
var readCtx = await readDbContextFactory.CreateDbContextAsync();
await readCtx.Database.MigrateAsync();
return serviceProvider;
}
}
CI/CD Pipeline Integration
Azure DevOps Pipeline
- task: DotNetCoreCLI@2
displayName: 'Apply Database Migrations'
inputs:
command: 'custom'
custom: 'tool'
arguments: 'run peardrop migrate'
workingDirectory: '$(Build.SourcesDirectory)'
Azure Pipelines
- task: DotNetCoreCLI@2
displayName: 'Apply Database Migrations'
inputs:
command: 'custom'
custom: 'tool'
arguments: 'run peardrop migrate'
workingDirectory: '$(Build.SourcesDirectory)'
Docker Container Startup
# In your Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app
COPY . .
RUN dotnet tool restore
RUN dotnet build
# In your entrypoint script
ENTRYPOINT ["sh", "-c", "dotnet tool run peardrop migrate && dotnet run"]
Troubleshooting
"peardrop: command not found"
Restore the CLI tool:
dotnet tool restore
Or use the full path:
dotnet tool run peardrop migrate
Connection String Not Found
Verify appsettings.json contains:
{
"ConnectionStrings": {
"PearDrop": "Server=..."
}
}
Check the logs for connection string resolution order.### Database Connection Failed
Ensure SQL Server is running:
# Check Docker container status
docker ps
# Start SQL Server if not running
docker-compose up -d
Verify connection string has correct credentials and server address.
Migration Already Applied Error
This usually means the migration history table is out of sync. Check applied migrations in the database:
-- Query EF Core migrations history
SELECT * FROM [__EFMigrationsHistory] ORDER BY MigrationId DESC;
If needed, manually add/remove entries, or use a fresh database for development.
Best Practices
✅ DO
- Use PearDrop CLI for local development:
peardrop migrate - Automate migrations in CI/CD pipelines before deployment
- Test migrations in a staging environment before production
- Version control migrations - commit all migration files
- Separate write/read migrations - PearDrop CLI handles this automatically
❌ DON'T
- Don't use
dotnet ef database updatedirectly - usepeardrop migrateinstead - Don't manually edit migration files after applying them
- Don't skip migrations - always apply in sequential order
- Don't assume automatic application - be explicit (CLI or programmatic helper)
- Don't run migrations from multiple processes simultaneously (race conditions)
Next Steps
- Generate migrations: See Creating Migrations
- Write model setup: See Write Model DbContext
- Read model setup: See Read Models
- PearDrop CLI reference: See PearDrop CLI Guide