A .NET 8 command-line utility for restoring database backups to Microsoft SQL Server and PostgreSQL, with built-in archive extraction (7-Zip), pre-restore validation, interactive conflict resolution, and standalone query execution.
- Multi-server support — define multiple SQL Server / PostgreSQL instances in a single
config.json. - Archive handling — restores directly from
.7z,.zip, and.rararchives. Bundled 7-Zip binaries are auto-extracted to a temp folder at runtime (no external install needed). - Password-protected archives — tries a list of candidate passwords from config.
- Disk space validation — reads backup metadata and verifies sufficient free space before restoring.
- Conflict resolution — if the target database exists, prompts to replace, restore under a new name, or cancel (or use
--force-replaceto skip the prompt). - MSSQL specifics — reads logical file names via
RESTORE FILELISTONLY, moves data/log files to a configurableDataLocation, kills active connections, drops the target DB, and streams real-time restore progress (STATS = 5). - PostgreSQL specifics — terminates active connections, drops/creates the target database, and shells out to
psql(for.sqldumps) orpg_restore(for.dump/.backup/ binary dumps). - Query mode — run a standalone
.sqlfile against a database and export every result set to timestamped CSV files. - Post-restore query — optionally runs a verification query after a restore and writes results to a configured output folder.
- Orphaned file detection — catches orphaned
.mdf/.ldffiles that would cause "Access Denied" on restore.
- .NET 8 SDK to build/run from source.
- Windows (project is published as
win-x64self-contained single-file). - PostgreSQL restore additionally requires
psqlandpg_restoreonPATH(and they must be installed on the machine). .NET 8.0runtime for the published binary.
dotnet build -c Release
dotnet publish -c Release -r win-x64The project publishes as a self-contained single file:
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>The zip/ folder contents (7z.exe, 7z.dll) are embedded as resources and extracted to %TEMP%\db_restore_tool\7z\ at runtime.
db_restore_tool <backup_file> [target_db] [options]
db_restore_tool -q <query_file> -d <target_db> [options]
| Option | Description |
|---|---|
-s, --server <name> |
Target server from config (defaults to DefaultServer in config). |
-d, --database <name> |
Target database name (overrides positional argument). |
-q, --query <path> |
Run a standalone query instead of a restore. |
-o, --output <path> |
Output directory for query results (defaults to QueryResultsPath). |
-f, --force-replace |
Force replace an existing database without prompting. |
-h, --help |
Show help. |
Restore a .bak to the default server, deriving the database name from the filename:
db_restore_tool C:\backups\MyApp.bakRestore an archive to a named server and database, replacing without prompting:
db_restore_tool C:\backups\MyApp.7z -s prod-mssql -d MyApp -fRun a query and export CSV results:
db_restore_tool -q C:\queries\report.sql -d MyApp -o C:\resultsIf no server is specified (and no DefaultServer), an interactive engine/server picker is shown. If a .sql file is passed as the positional argument without -q, query mode is auto-detected.
config.json lives next to the executable. If missing, a default template is generated and the app exits, telling you to configure it and rerun.
{
"DefaultServer": "local-mssql",
"Servers": [
{
"Name": "local-mssql",
"Type": "MSSQL",
"ServerName": "RG-PC",
"Username": "sa",
"Password": "Admin@123",
"DataLocation": "C:\\DbRestoreTool\\MSSQL-DATA"
},
{
"Name": "dev-postgres",
"Type": "PostgreSQL",
"Host": "localhost",
"Port": 5432,
"Username": "postgres",
"Password": "admin123",
"DataLocation": "C:\\DbRestoreTool\\PG-DATA"
}
],
"TempDirectory": "C:\\DbRestoreTool\\TempDB",
"QueryPath": "C:\\DbRestoreTool\\query.sql",
"QueryResultsPath": "C:\\DbRestoreTool\\Result",
"ZipPassword": [ "", "" ]
}| Field | Description |
|---|---|
DefaultServer |
Server used when -s is omitted. |
Servers[] |
List of server definitions (see below). |
TempDirectory |
Root temp folder; each job gets an isolated GUID subfolder for extraction. |
QueryPath |
Optional SQL file run automatically after a successful restore. |
QueryResultsPath |
Default output folder for query/result CSV exports. |
ZipPassword |
Candidate passwords tried when extracting protected archives (may be a single string or array). |
| Field | Applies to | Description |
|---|---|---|
Name |
both | Unique identifier used by -s. |
Type |
both | MSSQL or PostgreSQL. |
ServerName |
MSSQL | SQL Server instance (e.g. RG-PC). |
Host |
PostgreSQL | Postgres hostname/IP. |
Port |
PostgreSQL | Postgres port (default 5432 if omitted). |
Username |
both | Login (SQL auth for MSSQL, or Windows auth is attempted as fallback). |
Password |
both | Password. |
DataLocation |
both | Directory where data files land (*.mdf/*.ldf for MSSQL). |
Note: MSSQL connection tries SQL Authentication first, then Windows Authentication (
IntegratedSecurity=true). Restore file paths must be visible to the SQL Server service account.
- Read config & select server — parse
config.json, resolve the target server (flag, default, or interactive picker). - Verify connection —
EnsureConnectionAsyncagainst the server. - Validate backup file — existence + size.
- Extract archive — if input is
.7z/.zip/.rar, extract to an isolated temp dir using embedded 7-Zip; locate the restore file (.bakfor MSSQL;.sql/.dump/.backupfor PostgreSQL). Otherwise the file is used directly. - Read backup metadata — MSSQL uses
RESTORE HEADERONLY+RESTORE FILELISTONLYto get the original database name, logical file names, and required space; PostgreSQL estimates from file size. - Disk space check — compare required vs. available space on the drive hosting
DataLocation. - Conflict resolution — if the database exists, ask
1(replace) /2(new name) /3(cancel) unless-fis given. For MSSQL, orphaned.mdf/.ldffiles are also detected. - Restore — kill connections, drop existing DB, then run the restore with live progress (
STATS = 5percent messages). PostgreSQL creates the DB then runspsql/pg_restore. - Summary & post-query — log elapsed time and final size, copy DB name to clipboard, optionally run
QueryPath.
- Executes the whole
.sqlfile against the chosen database with an unlimited command timeout. - Each result set is exported to
Result_<timestamp>_<id>_<n>.csvin the output folder (UTF-8, CSV-escaped). Non-result commands report(n rows affected).
| Layer | Location | Responsibility |
|---|---|---|
| Entry point | Program.cs |
CLI parsing, DI bootstrap, interactive selection, orchestration. |
| Models | Models/ |
AppConfig, ServerConfig, BackupMetadata, RestoreContext, RestoreJob, JSON converter. |
| Config | Config/ConfigService.cs |
Load/validate config.json, generate default template, detect old format. |
| Common | Common/ |
DiskSpaceValidator, FileSystemService (filesystem abstraction), JobLogger/ConsoleLogger (logging + progress bar + prompts + clipboard). |
| Database | Database/ |
IDatabaseProvider abstraction, DatabaseProviderFactory, and MSSQL / PostgreSQL implementations. |
| Database.Mssql | Database/Mssql/ |
MssqlConnectionProvider + Commands/ (drop, kill connections, restore) + Queries/ (exists, size, metadata). |
| Database.Postgres | Database/Postgres/ |
PostgresConnectionProvider + provider logic incl. psql/pg_restore invocation. |
| Extraction | Extraction/ |
ArchiveExtractionService (7-Zip extraction with password fallback + progress), SevenZipBootstrapper (extracts embedded 7z.exe/7z.dll). |
| Restore | Restore/DatabaseRestoreService.cs |
The 8-step restore orchestration. |
| Services | Services/QueryExecutorService.cs |
Result-set iteration and CSV export. |
IDatabaseProvider— the database abstraction used by the restore service:
string ProviderType { get; }
Task EnsureConnectionAsync(CancellationToken);
Task<bool> DatabaseExistsAsync(string dbName, CancellationToken);
Task<double> GetDatabaseSizeMbAsync(string dbName, CancellationToken);
Task<BackupMetadata> GetBackupMetadataAsync(string backupFilePath, CancellationToken);
Task KillConnectionsAsync(string dbName, CancellationToken);
Task DropDatabaseAsync(string dbName, CancellationToken);
Task RestoreDatabaseAsync(string dbName, string backupFilePath, BackupMetadata, Action<string>, CancellationToken);
Task<List<string>> GetDatabasesAsync(CancellationToken);
Task ExecuteQueryAsync(string dbName, string queryFilePath, string outputDir, Action<string>, CancellationToken);IConsoleLogger— logging/progress/prompt surface (thread-safe via a shared lock).IArchiveExtractionService,IDiskSpaceValidator,IFileSystemService— swap-friendly service abstractions registered viaMicrosoft.Extensions.DependencyInjection.
.
├── Program.cs # Entry point, CLI parsing, DI, orchestration
├── db_restore_tool.csproj # .NET 8, win-x64 single-file publish
├── db_restore_tool.sln
├── Config/
│ └── ConfigService.cs # config.json load/validate/generate
├── Models/
│ ├── AppConfig.cs
│ ├── ServerConfig.cs
│ ├── BackupMetadata.cs
│ ├── RestoreContext.cs
│ ├── RestoreJob.cs
│ └── SingleOrArrayConverter.cs
├── Common/
│ ├── ConsoleLogger.cs # IConsoleLogger + implementation
│ ├── JobLogger.cs # Thread-safe console logger
│ ├── DiskSpaceValidator.cs
│ └── FileSystemService.cs
├── Database/
│ ├── IDatabaseProvider.cs
│ ├── DatabaseProviderFactory.cs
│ ├── Mssql/
│ │ ├── MssqlConnectionProvider.cs
│ │ ├── MssqlDatabaseProvider.cs
│ │ ├── Commands/ (Drop, KillConnections, Restore)
│ │ └── Queries/ (CheckDatabaseExists, GetBackupMetadata, GetDatabaseSize)
│ └── Postgres/
│ ├── PostgresConnectionProvider.cs
│ └── PostgresDatabaseProvider.cs
├── Extraction/
│ ├── ArchiveExtractionService.cs
│ └── SevenZipBootstrapper.cs
├── Restore/
│ └── DatabaseRestoreService.cs
├── Services/
│ └── QueryExecutorService.cs
└── zip/ # Embedded 7z.exe / 7z.dll resources
RestoreContext.IsExplicitTargetNameSelected()always returnstrue(simplified); the metadata database name only overrides the target when the target equals the input file's base name.- PostgreSQL restore relies on
psql/pg_restorebeing installed and onPATH; the password is passed via thePGPASSWORDenvironment variable. DropDatabaseCommandsilently tolerates error 3 ("operating system error 3") so aRESTORE ... WITH REPLACEcan overwrite orphaned records.- Archive extraction only looks in the top directory of the archive for restore files.
- The clipboard copy uses a PowerShell
Set-Clipboardsubprocess (Windows only). - Older single-server
config.jsonformat (with a top-levelServerName) is detected and rejected — delete the file to regenerate.