Schema Migrations
Weasel's migration system detects differences between your configured schema objects and the actual state of a live database, then generates and optionally applies the DDL needed to bring the database in line. The process is fully automated and works across all supported providers.
Migration Flow
The IDatabase Interface
IDatabase (in Weasel.Core.Migrations) is the central interface for managing a database's schema lifecycle:
public interface IDatabase_Sample
{
AutoCreate AutoCreate { get; }
Migrator Migrator { get; }
string Identifier { get; }
List<string> TenantIds { get; }
IFeatureSchema[] BuildFeatureSchemas();
string[] AllSchemaNames();
IEnumerable<ISchemaObject> AllObjects();
Task<SchemaMigration> CreateMigrationAsync(CancellationToken ct = default);
Task<SchemaMigration> CreateMigrationAsync(IFeatureSchema group, CancellationToken ct = default);
Task<SchemaPatchDifference> ApplyAllConfiguredChangesToDatabaseAsync(
AutoCreate? @override = null,
ReconnectionOptions? reconnectionOptions = null,
CancellationToken ct = default);
Task AssertDatabaseMatchesConfigurationAsync(CancellationToken ct = default);
string ToDatabaseScript();
}Key methods:
| Method | Purpose |
|---|---|
BuildFeatureSchemas() | Returns all feature schemas in dependency order. |
CreateMigrationAsync() | Compares configured objects against the live database and returns a SchemaMigration. |
ApplyAllConfiguredChangesToDatabaseAsync() | Detects changes and applies them, respecting the AutoCreate policy. |
AssertDatabaseMatchesConfigurationAsync() | Throws if the database does not match configuration. Useful for production startup checks. |
ToDatabaseScript() | Returns the full DDL creation script as a string. |
IFeatureSchema
An IFeatureSchema groups related schema objects together (for example, all the tables and indexes for a document storage feature):
public interface IFeatureSchema_Sample
{
ISchemaObject[] Objects { get; }
string Identifier { get; }
Migrator Migrator { get; }
Type StorageType { get; }
}Weasel processes features in the order returned by BuildFeatureSchemas(), so dependency relationships between features should be reflected by their position in the array.
SchemaMigration
The SchemaMigration class aggregates deltas from multiple schema objects into a single migration result:
var migration = await database.CreateMigrationAsync();
// Check the overall result
if (migration.Difference == SchemaPatchDifference.None)
{
// Database is up to date
}SchemaMigration exposes the collection of ISchemaObjectDelta instances and computes the aggregate Difference as the minimum (most severe) difference across all deltas.
AutoCreate Policy
The AutoCreate enum (from the JasperFx namespace) controls what schema changes Weasel is allowed to make at runtime:
| Value | Behavior | Recommended Use |
|---|---|---|
All | Creates, updates, and recreates objects as needed. May drop and rebuild tables that cannot be incrementally updated. | Development and testing. |
CreateOrUpdate | Creates missing objects and applies incremental updates. Never drops existing objects. | Staging or early production deployments. |
CreateOnly | Creates missing objects only. Will not modify existing objects. | Controlled deployments. |
None | No runtime schema changes. Throws if the database does not match. | Production with CI/CD-managed migrations. |
Set the policy on your database instance:
// In development -- let Weasel manage everything
database.AutoCreate = AutoCreate.All;
// In production -- fail fast if the schema is wrong
database.AutoCreate = AutoCreate.None;You can also override the policy for a single call:
await database.ApplyAllConfiguredChangesToDatabaseAsync(
@override: AutoCreate.CreateOrUpdate
);The Migrator
Each database provider has a Migrator subclass that knows how to format SQL for that engine:
PostgresqlMigrator-- wraps DDL in transactions, handlesCREATE SCHEMA IF NOT EXISTSSqlServerMigrator-- usesGObatch separators, handlesdboschema conventionsOracleMigrator-- Oracle-specific DDL formattingSqliteMigrator-- simplified DDL without schema creation SQL (SQLite schemas are fixed)
The Migrator is used internally by WriteCreateStatement(), WriteDropStatement(), and WriteUpdate() on every schema object and delta.
Putting It Together
A typical migration workflow in application startup:
// 1. Configure your database with schema objects
var database = new MyPostgresqlDatabase(dataSource);
// 2. Apply all changes (respects AutoCreate policy)
var result = await database.ApplyAllConfiguredChangesToDatabaseAsync();
// result is SchemaPatchDifference.None if no changes were neededFor CI/CD pipelines, you can generate migration scripts without applying them:
// Generate a migration script file
await database.WriteMigrationFileAsync("migrations/next.sql");
// Or get the full creation script
var script = database.ToDatabaseScript();Migration Logging
Implement IMigrationLogger to capture the SQL that Weasel generates:
public interface IMigrationLogger_Sample
{
void SchemaChange(string sql);
void OnFailure(DbCommand command, Exception ex);
}The default logger writes SQL to Console.WriteLine and rethrows exceptions.
