Migrations
Handcrafted and auto-generated schema evolution with safe fallbacks.
Overview
RoomSharp keeps schema changes aligned with your RoomDatabase version. The builder wires
both handcrafted manual migrations and declarative auto migrations
generated from attributes on your database type.
- Manual migrations let you run any SQL or complex data fixes.
- Auto migrations use attributes like
[AutoMigration],[RenameTable], and[ColumnRename]to emit SQL for common changes. - Fallback destructive mode can drop and recreate tables for dev/test downgrades.
Metadata table
RoomSharp tracks schema state in __room_metadata. MigrationManager applies
pending migrations up to the [Database(Version = N)] declared on your database
implementation.
Registering migrations
// Database declaration with version
[Database(Version = 3, Entities = new[] { typeof(User) })]
public abstract class AppDatabase : RoomDatabase
{
protected AppDatabase(IDatabaseProvider provider, ILogger? logger = null)
: base(provider, logger) { }
}
// Builder wiring (add this in your composition root)
var db = RoomDatabase.Builder()
.UseSqlite(\"app.db\")
.AddMigrations(new UserMigration_1_2(), new UserMigration_2_3())
.Build();
Manual migrations
Create a class that inherits Migration, then implement Migrate. You can run
any SQL and execute multiple statements per step.
public sealed class UserMigration_1_2() : Migration(1, 2)
{
public override void Migrate(DbConnection connection)
{
using var cmd = connection.CreateCommand();
cmd.CommandText = \"ALTER TABLE users ADD COLUMN status TEXT DEFAULT 'active'\";
cmd.ExecuteNonQuery();
}
}
Add the migration to the builder via AddMigrations. Migrations are executed in order and
must form a continuous chain (1→2, 2→3, ...).
Auto migrations
Mark your database with [AutoMigration] attributes for version jumps. You can include an
optional IAutoMigrationSpec for declarative tweaks and post-migrate hooks.
[Database(Version = 3, Entities = new[] { typeof(User) })]
[AutoMigration(From = 1, To = 2, Spec = typeof(UserTableSpec))]
[AutoMigration(From = 2, To = 3)]
public abstract class UserDatabase : RoomDatabase
{
protected UserDatabase(IDatabaseProvider provider, ILogger? logger = null) : base(provider, logger) { }
}
[RenameTable(FromTableName = \"old_users\", ToTableName = \"users\")]
[DeleteColumn(TableName = \"users\", ColumnName = \"legacy_flag\")]
public sealed class UserTableSpec : IAutoMigrationSpec
{
[ColumnRename(TableName = \"users\", FromColumnName = \"full_name\", ToColumnName = \"name\")]
public string UsersTable => \"users\";
public void OnPostMigrate(SqliteConnection connection)
{
using var cmd = connection.CreateCommand();
cmd.CommandText = \"UPDATE users SET status='active' WHERE status IS NULL\";
cmd.ExecuteNonQuery();
}
}
Auto migrations are emitted at build time and executed before manual migrations that start at the same version.
Fallback to destructive migration
For local development or test environments you can allow destructive downgrades if no matching migration chain exists.
var db = RoomDatabase.Builder()
.UseSqlite(\"app.db\")
.FallbackToDestructiveMigration()
.Build();
When enabled, RoomSharp drops user tables and recreates them from entity metadata, then resets
__room_metadata. Use cautiously and avoid in production.
Callbacks and diagnostics
MigrationManager.ApplyMigrationslogs each migration step; provide anILoggerto see details.RoomDatabase.OnCreateandOnMigratecallbacks receive a flag indicating if a destructive migration occurred.- You can query
db.GetCurrentVersion()to inspect the stored schema version.
- Keep versions contiguous—no gaps between
StartVersionandEndVersion. - Run destructive fallback only in non-production environments.
- Use auto migrations for schema shape changes, manual migrations for data backfills or complex SQL.

