LowCodeHub.Migration.PostgreSql
0.0.12
dotnet add package LowCodeHub.Migration.PostgreSql --version 0.0.12
NuGet\Install-Package LowCodeHub.Migration.PostgreSql -Version 0.0.12
<PackageReference Include="LowCodeHub.Migration.PostgreSql" Version="0.0.12" />
<PackageVersion Include="LowCodeHub.Migration.PostgreSql" Version="0.0.12" />
<PackageReference Include="LowCodeHub.Migration.PostgreSql" />
paket add LowCodeHub.Migration.PostgreSql --version 0.0.12
#r "nuget: LowCodeHub.Migration.PostgreSql, 0.0.12"
#:package LowCodeHub.Migration.PostgreSql@0.0.12
#addin nuget:?package=LowCodeHub.Migration.PostgreSql&version=0.0.12
#tool nuget:?package=LowCodeHub.Migration.PostgreSql&version=0.0.12
LowCodeHub.Migration.PostgreSql
A startup migration runner for PostgreSQL using embedded .sql scripts and deterministic execution order. Scripts are ordered by Unix timestamp prefix, executed in individual transactions, and journaled by DbUp.
Why This Library?
| Feature | LowCodeHub.Migration.PostgreSql | EF Core Migrations | Raw DbUp |
|---|---|---|---|
| Script format | Plain SQL files | C# migration classes | Plain SQL files |
| Ordering | Unix timestamp prefix | Sequential migration IDs | Alphabetical |
| Multiple databases | Target per scanner | Manual setup | Manual setup |
| Fail-fast | Strict naming validation | Runtime errors | Easy to miss |
| Always-execute | Built-in directory support | Manual | Manual |
| Transactions | Per-script | Per-migration | Per-script |
| DI integration | AddPostgreSqlMigrations() + RunPostgreSqlMigrationAsync() |
Database.Migrate() |
Manual |
Installation
dotnet add package LowCodeHub.Migration.PostgreSql
Quick Start
using LowCodeHub.Migration.PostgreSql.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPostgreSqlMigrations(migrations =>
{
migrations.AddTarget<UsersScriptScanner>(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("UsersDb")!;
options.Directories = ["MyApp.Users.Migrations"];
});
});
var app = builder.Build();
await app.RunPostgreSqlMigrationAsync(app.Lifetime.ApplicationStopping);
app.Run();
The scanner type is still required when adding a target because it identifies the assembly that contains embedded SQL resources. You do not pass scanners when running migrations; registered targets are enough.
Multiple PostgreSQL Targets
Register one target per scanner/database pair:
builder.Services.AddPostgreSqlMigrations(migrations =>
{
migrations.AddTarget<UsersScriptScanner>(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("UsersDb")!;
options.Directories = ["MyApp.Users.Migrations"];
options.AlwaysExecuteDirectories = ["MyApp.Users.Always"];
});
migrations.AddTarget<BillingScriptScanner>(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("BillingDb")!;
options.Directories = ["MyApp.Billing.Migrations"];
});
});
await app.RunPostgreSqlMigrationAsync();
Targets run in registration order. Scripts are globally ordered only inside a single target. If two targets point at the same database, each target still uses its own scanner assembly and options, and the runner executes them sequentially.
Target-Based API
This package uses the target-based API only:
builder.Services.AddPostgreSqlMigrations(migrations =>
{
migrations.AddTarget<MyScriptScanner>(options =>
{
options.ConnectionString = "Host=localhost;Database=MyDb;Username=postgres;Password=yourpassword";
options.Directories = ["MyApp.Migrations.Scripts"];
});
});
await app.RunPostgreSqlMigrationAsync();
Script Naming Convention
Scripts must start with a numeric Unix timestamp prefix:
| Script Name | Valid? |
|---|---|
1766733156_create_users.sql |
Yes |
1766733160_add_index.sql |
Yes |
create_users.sql |
No, missing prefix |
v1_create_users.sql |
No, prefix is not numeric |
Ordering rules inside each target:
- Primary sort: numeric prefix ascending
- Secondary sort: full script resource name
- Directory name: used only for filtering
Target Options
Each target has its own MigrationOptions.
| Option | Default | Description |
|---|---|---|
ConnectionString |
required | PostgreSQL connection string for this target |
Directories |
null |
Embedded resource directories for journaled scripts |
AlwaysExecuteDirectories |
null |
Embedded resource directories for always-execute scripts |
FailOnNoScriptsFound |
true |
Throw if no scripts are found for this target |
EnsureDatabaseExists |
true |
Create the database if it does not exist. Requires the CREATEDB privilege |
UseDistributedLock |
true |
Hold a session-scoped pg_advisory_lock for the target migration run |
Journaled vs Always-Execute Scripts
| Type | Directory Config | Behavior |
|---|---|---|
| Journaled | Directories |
Run once and tracked by DbUp's journal table |
| Always-execute | AlwaysExecuteDirectories |
Run every startup and not tracked in the journal |
Always-execute scripts must be idempotent.
Execution Behavior
Migrations execute synchronously on the calling thread because DbUp has no async API. The Async method returns a completed task and exists for startup-code ergonomics.
For each target:
- Ensure the target database exists, unless disabled.
- Scan the target scanner assembly for embedded
.sqlresources. - Split scripts into journaled and always-execute groups.
- Order each group by Unix timestamp prefix.
- Acquire a PostgreSQL advisory lock.
- Execute each script in its own transaction.
If one target fails, migration stops and later targets are not executed.
Cancellation
Pass a CancellationToken to cancel between script executions:
await app.RunPostgreSqlMigrationAsync(app.Lifetime.ApplicationStopping);
A running script completes or rolls back before cancellation takes effect.
Best Practices
- Keep script names immutable once merged; renaming breaks the journal.
- Use Unix seconds prefixes for predictable ordering.
- Keep one concern per script.
- Use
AlwaysExecuteDirectoriesonly for idempotent scripts. - Keep
FailOnNoScriptsFound = truein production. - Embed SQL files as resources:
<EmbeddedResource Include="Scripts\**\*.sql" />
Requirements
- .NET 10 or later
dbup-postgresql7.2+ (included as a dependency)Npgsql7.0+ (included as a dependency)
License
MIT (c) Ahmed Abuelnour
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- dbup-postgresql (>= 7.0.1)
- Npgsql (>= 10.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.