A tiny console-oriented helper to run Entity Framework Core migrations in a predictable, CI/CD-friendly way. It parses a simple command line, executes pending migrations against your target database, and can optionally throw on error for strict pipelines.
- Library: PosInformatique.Database.Updater
- Install with:
dotnet add package PosInformatique.Database.Updater- Clean separation of concerns: keep migrations in a small console app dedicated to database updates, invoked by your CD pipeline.
- Consistent CLI: same arguments locally and in CI/CD, using standard .NET command-line syntax.
- EF Core under the hood: executes your existing EF Core migrations generated by developers.
- Entra ID token support: pass an access token for Entra ID authentication (perfect for Azure Pipelines).
- Logging support: uses the standard .NET logging abstraction, including EF Core logs, and writes to standard output.
Just create a simple console application:
public static class Program
{
public static async Task<int> Main(string[] args)
{
var updater = new DatabaseUpdaterBuilder("MyApplication")
.UseSqlServer()
.UseMigrationsAssembly(typeof(Program).Assembly)
.Build();
return await updater.UpgradeAsync(args);
}
}The code above runs the Entity Framework Core migrations located in the same assembly as the Program class.
Developers can run this application locally to upgrade the database, or integrate it into a CD pipeline when deploying an application.
The DatabaseUpdaterBuilder creates an internal IHost, allowing developers to configure additional services.
After configuring the DatabaseUpdaterBuilder, call Build() to get an IDatabaseUpdater, then
call IDatabaseUpdater.UpgradeAsync(string[]) to run the migration process with the args from Main().
IDatabaseUpdater.UpgradeAsync(string[]) returns an int error code that you can return from Main() and use in calling
scripts (e.g., %ERRORLEVEL%).
When instantiating DatabaseUpdaterBuilder, pass your application name.
This is only used for help/diagnostics in the command-line output and has no impact on the migration process.
For example:
var updater = new DatabaseUpdaterBuilder("MyApplication");Currently, only SQL Server is supported. To use SQL Server, call UseSqlServer() during builder setup.
For example:
var updater = new DatabaseUpdaterBuilder("MyApplication")
.UseSqlServer()
// ...
.Build();To point to the assembly that contains your EF Core migrations, call UseMigrationsAssembly() with the appropriate Assembly.
For example:
var updater = new DatabaseUpdaterBuilder("MyApplication")
.UseSqlServer()
.UseMigrationsAssembly(typeof(Program).Assembly)
// ...
.Build();To configure logging produced by IDatabaseUpdater, call ConfigureLogging() during builder setup.
This provides an ILoggingBuilder to configure the .NET logging infrastructure.
For example:
var updater = new DatabaseUpdaterBuilder("MyApplication")
.UseSqlServer()
.UseMigrationsAssembly(typeof(Program).Assembly)
.ConfigureLogging(builder =>
{
builder.AddJsonConsole();
})
// ...
.Build();To configure updater options, call Configure() during builder setup.
This provides a DatabaseUpdaterOptions instance to control behavior.
For example:
var updater = new DatabaseUpdaterBuilder("MyApplication")
.UseSqlServer()
.UseMigrationsAssembly(typeof(Program).Assembly)
.Configure(opt =>
{
opt.ThrowExceptionOnError = true;
})
// ...
.Build();When calling IDatabaseUpdater.UpgradeAsync() with the args from the command line, the following syntax is parsed:
dotnet run <ConsoleApp.dll> "<connection-string>" [--access-token "<access-token>"] [--command-timeout <seconds>]-
connection-string- Description: The connection string to the database to upgrade. It is recommended to wrap it in double quotes.
- Required: Yes
- Example:
dotnet run my-updater.dll "Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;"
-
Option:
--access-token- Description: Access token to connect to Azure SQL using Entra ID.
- Required: No
- Example:
dotnet run my-updater.dll "Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;" --access-token "xxxxxxxx"
-
Option:
--command-timeout- Description: Maximum time in seconds to execute each SQL statement. If not specified, the default is 30 seconds. Use this to extend timeouts for long-running migrations.
- Required: No
- Example:
dotnet run my-updater.dll "Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;" --command-timeout 600
IDatabaseUpdater.UpgradeAsync() returns the following error codes:
| Error code | Description |
|---|---|
| 0 | The database upgrade completed successfully. |
| 1 | An invalid command-line argument was provided. |
| 99 | An exception occurred during the upgrade process (when DatabaseUpdaterOptions.ThrowExceptionOnError = false). |
| -532462766 | Standard .NET unhandled exception code (when DatabaseUpdaterOptions.ThrowExceptionOnError = true). |
We recommend returning the error code from
IDatabaseUpdater.UpgradeAsync()directly fromMain().
By default, when an exception occurs (timeout, SQL syntax error, etc.), no exception is propagated. The exception is logged and IDatabaseUpdater.UpgradeAsync() returns error code 99.
To propagate exceptions, set DatabaseUpdaterOptions.ThrowExceptionOnError to true.
var updater = new DatabaseUpdaterBuilder("MyApplication")
.UseSqlServer()
.UseMigrationsAssembly(typeof(Program).Assembly)
.Configure(opt =>
{
opt.ThrowExceptionOnError = true; // Throw instead of returning error code 99.
})
// ...
.Build();During the upgrade, some SQL commands can take a long time—especially DML on large tables or DDL that rebuilds indexes.
To increase the per-command timeout, use the optional --command-timeout argument:
dotnet run my-updater.dll "Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;" --command-timeout 600Do not confuse the SQL command execution timeout with the SQL connection timeout (used to connect to SQL Server). To change the connection timeout, set
Connection Timeoutin the connection string. Example:"Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;Connection Timeout=600"
Recommended practice: keep migrations in a dedicated console app that references your DbContext and migrations assembly. Your release pipeline calls this console to upgrade the database.
Example Azure Pipelines YAML:
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '8.x'
- script: dotnet restore
displayName: Restore
- script: dotnet build -c Release
displayName: Build
# Acquire an Entra ID token for Azure SQL (adjust resource if needed)
- task: AzureCLI@2
displayName: Get Azure SQL access token
inputs:
azureSubscription: 'Your-Service-Connection'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
TOKEN=$(az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv)
echo "##vso[task.setvariable variable=SQL_ACCESS_TOKEN;issecret=true]$TOKEN"
# Run the updater console
- script: |
dotnet run --project src/YourUpdaterConsole/YourUpdaterConsole.csproj -- \
"Server=tcp:$(sql_server),1433;Initial Catalog=$(sql_database);Encrypt=True;" \
--access-token "$(SQL_ACCESS_TOKEN)" \
--command-timeout 600
displayName: Run database updater- Use
--access-tokenwith Entra ID to avoid embedding credentials (you can use SQL authentication with username/password, but ensure secrets are stored securely, e.g., in Azure Key Vault). - Set
ThrowExceptionOnError = truefor CI runs to fail fast on migration errors, display the stack trace in Azure Pipelines output, and enable developers to hit exceptions directly in Visual Studio during development. - Keep the updater console small and focused; it should reference the migrations assembly via
.UseMigrationsAssembly(...)or include the migrations itself.
- .NET 8.0 or later.
- EF Core 8.0 or later.
- SQL Server is currently the only supported provider.
- Supported: SQL Server.
- Roadmap: Other providers may be added in the future; for now, only SQL Server is available.
If you want to test (assert) your database migrations, consider using PosInformatique.Testing.Databases. This library provides tools for database unit/integration tests and includes a comparer to verify the migration state of a database.
This tool uses Microsoft .NET System.CommandLine library, which standardizes command-line syntax for .NET tools.
If you need additional switches, providers, or features, feel free to open an issue or PR.