Skip to content

Commit 2945aa4

Browse files
Fix the README.
1 parent 6c13fc4 commit 2945aa4

File tree

1 file changed

+283
-1
lines changed

1 file changed

+283
-1
lines changed

README.md

Lines changed: 283 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,284 @@
11
# PosInformatique.Database.Updater
2-
A simple console tool to apply Entity Framework Core migrations. Focused on SQL Server with support for AccessToken authentication. Outputs logs to the console, ideal for CI/CD scenarios.
2+
[![NuGet](https://img.shields.io/nuget/v/PosInformatique.Database.Updater)](https://www.nuget.org/packages/PosInformatique.Database.Updater/)
3+
[![NuGet downloads](https://img.shields.io/nuget/dt/PosInformatique.Database.Updater)](https://www.nuget.org/packages/PosInformatique.Database.Updater/)
4+
[![License](https://img.shields.io/github/license/Nonanti/MathFlow?style=flat-square)](LICENSE)
5+
[![Build Status](https://img.shields.io/github/actions/workflow/status/PosInformatique/PosInformatique.Database.Updater/github-actions-ci.yaml?style=flat-square)](https://github.com/PosInformatique/PosInformatique.Database.Updater/actions)
6+
[![.NET 8.0+](https://img.shields.io/badge/.NET-8.0%2B-512BD4?style=flat-square)](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0)
7+
8+
A tiny console-oriented helper to run Entity Framework Core migrations in a predictable, CI/CD-friendly way.
9+
It parses a simple command line, executes pending migrations against your target database, and can optionally throw
10+
on error for strict pipelines.
11+
12+
## Installing from NuGet
13+
14+
- Library: [PosInformatique.Database.Updater](https://www.nuget.org/packages/PosInformatique.Database.Updater/)
15+
- Install with:
16+
```bash
17+
dotnet add package PosInformatique.Database.Updater
18+
```
19+
20+
## Why use this?
21+
22+
- Clean separation of concerns: keep migrations in a small console app dedicated to database updates, invoked by your CD pipeline.
23+
- Consistent CLI: same arguments locally and in CI/CD, using standard .NET command-line syntax.
24+
- EF Core under the hood: executes your existing EF Core migrations generated by developers.
25+
- Entra ID token support: pass an access token for Entra ID authentication (perfect for Azure Pipelines).
26+
- Logging support: uses the standard .NET logging abstraction, including EF Core logs, and writes to standard output.
27+
28+
## Example usage
29+
30+
Just create a simple console application:
31+
32+
```csharp
33+
public static class Program
34+
{
35+
public static async Task<int> Main(string[] args)
36+
{
37+
var updater = new DatabaseUpdaterBuilder("MyApplication")
38+
.UseSqlServer()
39+
.UseMigrationsAssembly(typeof(Program).Assembly)
40+
.Build();
41+
42+
return await updater.UpgradeAsync(args);
43+
}
44+
}
45+
```
46+
47+
The code above runs the Entity Framework Core migrations located in the same assembly as the `Program` class.
48+
49+
Developers can run this application locally to upgrade the database, or integrate it into a CD pipeline when deploying
50+
an application.
51+
52+
## API usage
53+
54+
The `DatabaseUpdaterBuilder` creates an internal `IHost`, allowing developers to configure additional services.
55+
56+
After configuring the `DatabaseUpdaterBuilder`, call `Build()` to get an `IDatabaseUpdater`, then
57+
call `IDatabaseUpdater.UpgradeAsync(string[])` to run the migration process with the `args` from `Main()`.
58+
59+
`IDatabaseUpdater.UpgradeAsync(string[])` returns an `int` error code that you can return from `Main()` and use in calling
60+
scripts (e.g., `%ERRORLEVEL%`).
61+
62+
### Name of the application
63+
64+
When instantiating `DatabaseUpdaterBuilder`, pass your application name.
65+
This is only used for help/diagnostics in the command-line output and has no impact on the migration process.
66+
67+
For example:
68+
```csharp
69+
var updater = new DatabaseUpdaterBuilder("MyApplication");
70+
```
71+
72+
### Specify the database provider
73+
74+
Currently, only SQL Server is supported. To use SQL Server, call `UseSqlServer()` during builder setup.
75+
76+
For example:
77+
```csharp
78+
var updater = new DatabaseUpdaterBuilder("MyApplication")
79+
.UseSqlServer()
80+
// ...
81+
.Build();
82+
```
83+
84+
### Specify the assembly that contains Entity Framework Core migrations
85+
86+
To point to the assembly that contains your EF Core migrations, call `UseMigrationsAssembly()` with the appropriate `Assembly`.
87+
88+
For example:
89+
```csharp
90+
var updater = new DatabaseUpdaterBuilder("MyApplication")
91+
.UseSqlServer()
92+
.UseMigrationsAssembly(typeof(Program).Assembly)
93+
// ...
94+
.Build();
95+
```
96+
97+
### Logging configuration
98+
99+
To configure logging produced by `IDatabaseUpdater`, call `ConfigureLogging()` during builder setup.
100+
This provides an `ILoggingBuilder` to configure the .NET logging infrastructure.
101+
102+
For example:
103+
```csharp
104+
var updater = new DatabaseUpdaterBuilder("MyApplication")
105+
.UseSqlServer()
106+
.UseMigrationsAssembly(typeof(Program).Assembly)
107+
.ConfigureLogging(builder =>
108+
{
109+
builder.AddJsonConsole();
110+
})
111+
// ...
112+
.Build();
113+
```
114+
115+
### Configure options of the updater
116+
117+
To configure updater options, call `Configure()` during builder setup.
118+
This provides a `DatabaseUpdaterOptions` instance to control behavior.
119+
120+
For example:
121+
```csharp
122+
var updater = new DatabaseUpdaterBuilder("MyApplication")
123+
.UseSqlServer()
124+
.UseMigrationsAssembly(typeof(Program).Assembly)
125+
.Configure(opt =>
126+
{
127+
opt.ThrowExceptionOnError = true;
128+
})
129+
// ...
130+
.Build();
131+
```
132+
133+
## Parsed command line
134+
135+
When calling `IDatabaseUpdater.UpgradeAsync()` with the `args` from the command line, the following syntax is parsed:
136+
137+
```cmd
138+
dotnet run <ConsoleApp.dll> "<connection-string>" [--access-token "<access-token>"] [--command-timeout <seconds>]
139+
```
140+
141+
- `connection-string`
142+
- Description: The connection string to the database to upgrade. It is recommended to wrap it in double quotes.
143+
- Required: Yes
144+
- Example: `dotnet run my-updater.dll "Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;"`
145+
146+
- Option: `--access-token`
147+
- Description: Access token to connect to Azure SQL using Entra ID.
148+
- Required: No
149+
- Example: `dotnet run my-updater.dll "Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;" --access-token "xxxxxxxx"`
150+
151+
- Option: `--command-timeout`
152+
- Description: Maximum time in seconds to execute each SQL statement. If not specified, the default is 30 seconds.
153+
Use this to extend timeouts for long-running migrations.
154+
- Required: No
155+
- Example: `dotnet run my-updater.dll "Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;" --command-timeout 600`
156+
157+
### Error code returned
158+
159+
`IDatabaseUpdater.UpgradeAsync()` returns the following error codes:
160+
161+
| Error code | Description |
162+
| ---------- | ----------- |
163+
| 0 | The database upgrade completed successfully. |
164+
| 1 | An invalid command-line argument was provided. |
165+
| 99 | An exception occurred during the upgrade process (when `DatabaseUpdaterOptions.ThrowExceptionOnError = false`). |
166+
| -532462766 | Standard .NET unhandled exception code (when `DatabaseUpdaterOptions.ThrowExceptionOnError = true`). |
167+
168+
> We recommend returning the error code from `IDatabaseUpdater.UpgradeAsync()` directly from `Main()`.
169+
170+
## Advanced scenarios
171+
172+
### Throw an exception when an error occurs
173+
174+
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`.
175+
176+
To propagate exceptions, set `DatabaseUpdaterOptions.ThrowExceptionOnError` to `true`.
177+
178+
```csharp
179+
var updater = new DatabaseUpdaterBuilder("MyApplication")
180+
.UseSqlServer()
181+
.UseMigrationsAssembly(typeof(Program).Assembly)
182+
.Configure(opt =>
183+
{
184+
opt.ThrowExceptionOnError = true; // Throw instead of returning error code 99.
185+
})
186+
// ...
187+
.Build();
188+
```
189+
190+
### Increase the timeout for SQL command execution
191+
192+
During the upgrade, some SQL commands can take a long time—especially DML on large tables or DDL that rebuilds indexes.
193+
194+
To increase the per-command timeout, use the optional `--command-timeout` argument:
195+
196+
```cmd
197+
dotnet run my-updater.dll "Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;" --command-timeout 600
198+
```
199+
200+
> Do not confuse the SQL command execution timeout with the SQL connection timeout (used to connect to SQL Server).
201+
To change the connection timeout, set `Connection Timeout` in the connection string.
202+
Example: `"Server=tcp:myserver.database.windows.net,1433;Initial Catalog=mydb;Encrypt=True;Connection Timeout=600"`
203+
204+
## Typical CI/CD flow (Azure Pipelines)
205+
206+
Recommended practice: keep migrations in a dedicated console app that references your DbContext and migrations assembly.
207+
Your release pipeline calls this console to upgrade the database.
208+
209+
Example Azure Pipelines YAML:
210+
211+
```yaml
212+
trigger:
213+
- main
214+
215+
pool:
216+
vmImage: ubuntu-latest
217+
218+
steps:
219+
- task: UseDotNet@2
220+
inputs:
221+
packageType: 'sdk'
222+
version: '8.x'
223+
224+
- script: dotnet restore
225+
displayName: Restore
226+
227+
- script: dotnet build -c Release
228+
displayName: Build
229+
230+
# Acquire an Entra ID token for Azure SQL (adjust resource if needed)
231+
- task: AzureCLI@2
232+
displayName: Get Azure SQL access token
233+
inputs:
234+
azureSubscription: 'Your-Service-Connection'
235+
scriptType: 'bash'
236+
scriptLocation: 'inlineScript'
237+
inlineScript: |
238+
TOKEN=$(az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv)
239+
echo "##vso[task.setvariable variable=SQL_ACCESS_TOKEN;issecret=true]$TOKEN"
240+
241+
# Run the updater console
242+
- script: |
243+
dotnet run --project src/YourUpdaterConsole/YourUpdaterConsole.csproj -- \
244+
"Server=tcp:$(sql_server),1433;Initial Catalog=$(sql_database);Encrypt=True;" \
245+
--access-token "$(SQL_ACCESS_TOKEN)" \
246+
--command-timeout 600
247+
displayName: Run database updater
248+
```
249+
250+
## Recommendations
251+
252+
- Use `--access-token` with Entra ID to avoid embedding credentials
253+
(you can use SQL authentication with username/password, but ensure secrets are stored securely, e.g., in Azure Key Vault).
254+
- Set `ThrowExceptionOnError = true` for CI runs to fail fast on migration errors, display the stack trace in Azure Pipelines
255+
output, and enable developers to hit exceptions directly in Visual Studio during development.
256+
- Keep the updater console small and focused; it should reference the migrations assembly via `.UseMigrationsAssembly(...)`
257+
or include the migrations itself.
258+
259+
## Requirements
260+
261+
- .NET 8.0 or later.
262+
- EF Core 8.0 or later.
263+
- SQL Server is currently the only supported provider.
264+
265+
### Provider support
266+
267+
- Supported: SQL Server.
268+
- Roadmap: Other providers may be added in the future; for now, only SQL Server is available.
269+
270+
# Links
271+
272+
## PosInformatique.Testing.Databases
273+
274+
If you want to test (assert) your database migrations, consider using
275+
[PosInformatique.Testing.Databases](https://github.com/PosInformatique/PosInformatique.Testing.Databases).
276+
This library provides tools for database unit/integration tests and includes a comparer to verify the migration state of a database.
277+
278+
## System.CommandLine
279+
280+
This tool uses Microsoft .NET [System.CommandLine](https://learn.microsoft.com/en-us/dotnet/standard/commandline/) library, which standardizes command-line syntax for .NET tools.
281+
282+
# Contribute
283+
284+
If you need additional switches, providers, or features, feel free to open an issue or PR.

0 commit comments

Comments
 (0)