SchemaDiscovery.Tool
1.0.27
dotnet tool install --global SchemaDiscovery.Tool --version 1.0.27
dotnet new tool-manifest
dotnet tool install --local SchemaDiscovery.Tool --version 1.0.27
#tool dotnet:?package=SchemaDiscovery.Tool&version=1.0.27
nuke :add-package SchemaDiscovery.Tool --version 1.0.27
schema-discovery
A .NET CLI tool that connects to a database, scans its schema (tables, views,
stored procedures, functions), and writes each object out as its own JSON
file — one file per object, e.g. Customers.json, dbo.OrderLines.json.
SQL Server is fully implemented. PostgreSQL and MySQL are scaffolded as extension points (see below) so support can be added without touching the CLI or the SQL Server code.
Project layout
SchemaDiscoveryV1.sln
src/
SchemaDiscovery.Cli/ 01 - CLI: argument parsing, Autofac wiring, JSON export
SchemaDiscovery/ 02 - Core: schema models, provider interfaces, Humanizer, persistence
SchemaDiscovery.Models/ Legacy models project, kept temporarily but no longer referenced
by anything — its classes now live in SchemaDiscovery (see below)
SchemaDiscovery.Providers.SqlServer/ 03 - Full SQL Server implementation (Microsoft.Data.SqlClient)
SchemaDiscovery.Providers.PostgreSql/ 03 - Stub — throws NotImplementedException, ready to fill in
SchemaDiscovery.Providers.MySql/ 03 - Stub — throws NotImplementedException, ready to fill in
SchemaDiscovery.Client/ 04 - .NET Framework 4.7.2 library that reads the exported JSON back
SchemaDiscovery.Client.Tests/ 05 - Tests for SchemaDiscovery.Client
SchemaDiscovery.Tests/ 05 - Tests for SchemaDiscovery (NUnit)
Note:
SchemaDiscovery.Modelsis mid-deprecation — its classes (TableSchema,ColumnDefinition, etc., namespaceSchemaDiscovery.Models) were moved into theSchemaDiscoveryproject. The project is kept in the solution for now but nothing references it anymore; it's expected to be deleted in a follow-up.
The design is a simple strategy/factory pattern:
IDatabaseSchemaProvider(inSchemaDiscovery) is the contract every database engine implements:GetTablesAsync,GetViewsAsync,GetStoredProceduresAsync,GetFunctionsAsync.IDatabaseSchemaProviderFactorycreates a provider instance given a connection string, anILoggerFactory, and the outputCultureLanguages.SchemaDiscovery.Cli.ProviderFactoryis a small registry that maps a--providername (sqlserver,postgres,mysql) to the right factory; factories are registered with Autofac inSchemaDiscovery.Cli/DependencyResolution/DefaultModule.cs.SchemaExportServiceis provider-agnostic: it just calls the interface and serializes whatever comes back, so it never needs to change when a new provider is added.
Building
Requires the .NET 10 SDK.
dotnet restore
dotnet build
Running
dotnet run --project src/SchemaDiscovery.Cli -- \
-c "Server=localhost;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;" \
-o ./schema-output
Or, after dotnet publish, run the produced schema-discovery executable directly:
schema-discovery -c "Server=.;Database=MyDb;User Id=sa;Password=***;" -p sqlserver -v
Installing as a .NET tool
SchemaDiscovery.Cli is set up to be packed and installed as a .NET tool,
so it can be run as schema-discovery from anywhere without dotnet run.
Pack it:
dotnet pack src/SchemaDiscovery.Cli -c Release
This produces src/SchemaDiscovery.Cli/nupkg/SchemaDiscovery.Tool.1.0.0.nupkg.
Install it globally from that local package folder:
dotnet tool install --global SchemaDiscovery.Tool --add-source ./src/SchemaDiscovery.Cli/nupkg
Or install it locally into a repo (creates/uses a .config/dotnet-tools.json manifest):
dotnet new tool-manifest # only if one doesn't already exist
dotnet tool install SchemaDiscovery.Tool --add-source ./src/SchemaDiscovery.Cli/nupkg
Once installed, run it directly:
schema-discovery -c "Server=localhost;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;" -o ./schema-output
To publish it for others to install without a local package source, push the
.nupkg to nuget.org or a private feed, then:
dotnet tool install --global SchemaDiscovery.Tool
To upgrade or remove:
dotnet tool update --global SchemaDiscovery.Tool
dotnet tool uninstall --global SchemaDiscovery.Tool
SchemaDiscovery.Client (reading the JSON output from code)
SchemaDiscovery.Client (in SchemaDiscovery.Client/, .NET Framework 4.7.2)
reads the JSON files schema-discovery writes back into typed TableSchema
/ ViewSchema / RoutineSchema objects, for tools — code generators, ORM
scaffolders — that want to consume a scanned schema without parsing JSON
themselves:
dotnet add package SchemaDiscovery.Client
var project = SchemaDiscovery.Client.ProjectLoader.LoadProject(@".\schema-output");
Being a classic .NET Framework 4.7.2 library, it doesn't take a package
dependency on the (net10.0-only) SchemaDiscovery project; instead it keeps
its own copy of the model types under SchemaDiscovery.Client/Models/. See
SchemaDiscovery.Client/README.md for the full API.
Releasing SchemaDiscovery.Cli / SchemaDiscovery.Client
There is currently no CI workflow that publishes packages automatically — both are packed and pushed by hand:
dotnet pack src/SchemaDiscovery.Cli -c Release # -> nupkg/SchemaDiscovery.Tool.<version>.nupkg
dotnet pack src/SchemaDiscovery.Client -c Release # -> nupkg/SchemaDiscovery.Client.<version>.nupkg
dotnet nuget push src/SchemaDiscovery.Cli/nupkg/SchemaDiscovery.Tool.<version>.nupkg --source https://api.nuget.org/v3/index.json --api-key <key>
dotnet nuget push src/SchemaDiscovery.Client/nupkg/SchemaDiscovery.Client.<version>.nupkg --source https://api.nuget.org/v3/index.json --api-key <key>
Bump <Version> in SchemaDiscovery.Cli.csproj (PackageId
SchemaDiscovery.Tool) and SchemaDiscovery.Client.csproj (PackageId
SchemaDiscovery.Client) before each release; both currently sit at 1.0.0.
Options
| Flag | Description |
|---|---|
-c, --connection-string |
Database connection string (required) |
-p, --provider |
sqlserver (default), postgres, or mysql |
-o, --output |
Output folder for JSON files (default ./schema-output) |
-l, --language |
Output language for generated text: en (default) or es |
-v, --verbose |
Enable debug-level console logging |
--skip-views |
Don't export views |
--skip-procedures |
Don't export stored procedures |
--skip-functions |
Don't export functions |
-h, --help |
Show usage |
Output
A projectInfo.json file is written at the root of the output folder with
scan metadata (providerName, scannedAtUtc, cultureLanguage). Every
table, view, stored procedure, and function is then written as its own JSON
file into a subfolder by kind — tables/, views/, stored-procedures/,
functions/. Objects in the default schema (dbo for SQL Server, public
for Postgres) are named <ObjectName>.json; objects in any other schema are
named <Schema>.<ObjectName>.json to avoid collisions.
Example tables/Customers.json (trimmed):
{
"schema": "dbo",
"name": "Customers",
"className": "Customer",
"pluralClassName": "Customers",
"qualifiedName": "dbo.Customers",
"objectType": "Table",
"columns": [
{ "ordinalPosition": 1, "name": "Id", "dataType": "int", "isNullable": false, "isIdentity": true, "isPrimaryKey": true },
{ "ordinalPosition": 2, "name": "Email", "dataType": "nvarchar", "maxLength": 255, "isNullable": false, "isPrimaryKey": false }
],
"primaryKeyColumns": ["Id"],
"foreignKeys": [],
"indexes": [
{ "name": "IX_Customers_Email", "isUnique": true, "isPrimaryKey": false, "indexType": "NONCLUSTERED", "columns": ["Email"] }
],
"rowCountEstimate": 4213
}
className/pluralClassName/displayName/pluralDisplayName (and a
column's propertyName/description) are omitted entirely until you set
them by hand — nulls aren't written to the file — and once set, they survive
future scans. See "Custom properties that survive a schema refresh" below.
Custom properties that survive a schema refresh
Every scanned property (column type, indexes, row counts, ...) is
regenerated on every run — that's the point of a scanner. But some fields
are metadata a person wants to type in by hand and keep, e.g. the class
name to generate for a table. Those are marked with [Persist]:
public class TableSchema : SchemaObjectBase
{
[Persist]
public string? ClassName { get; set; }
[Persist]
public string? PluralClassName { get; set; }
}
ColumnDefinition has the same idea with PropertyName and Description.
How it works: before SchemaExportService overwrites dbo.Customers.json,
it reads the existing file (if any) and — via SchemaPersistence in
SchemaDiscovery — copies the current value of every
[Persist] property from that file onto the freshly scanned object. Columns
are matched between the old and new file by Name, so per-column persisted
values (like PropertyName) are preserved too, not just table-level ones.
Only properties tagged [Persist] are carried forward; everything else is
always the fresh, scanned value.
Typical workflow:
- Run
schema-discovery—tables/Customers.jsonis created without aclassName. - Hand-edit the file, adding
"className": "Customer". - Run
schema-discoveryagain (schema changed, or just re-running) —"className": "Customer"is still there; everything else reflects the current database schema.
If a table or column is renamed or dropped in the database, its old
persisted values have nothing to match onto and are lost — there's no way to
infer that Customer should map to a renamed table without another signal.
To persist additional custom values, add a property with [Persist] to
TableSchema, ColumnDefinition, or any other model (ViewSchema,
RoutineSchema) — no other code changes are needed, since
SchemaPersistence discovers [Persist] properties via reflection.
Adding a new provider (e.g. finishing PostgreSQL or MySQL)
- Open
SchemaDiscovery.Providers.PostgreSql(or.MySql) — the project already exists and is wired into the CLI and solution. - Uncomment/add the relevant ADO.NET package in its
.csproj(NpgsqlorMySqlConnector). - Implement
GetTablesAsync,GetViewsAsync,GetStoredProceduresAsync, andGetFunctionsAsyncin the provider class, queryinginformation_schema/ the engine's catalog views. UseSqlServerSchemaProviderinSchemaDiscovery.Providers.SqlServeras a reference implementation — the shape (list objects, then fetch columns/keys/indexes per object) carries over directly. - No changes are needed in
SchemaDiscovery.Cli— the provider is already registered inDefaultModuleand selectable via-p postgres/-p mysql.
To add a brand-new engine entirely (e.g. Oracle, SQLite):
- Create a new class library project under
src/, referencingSchemaDiscovery. - Implement
IDatabaseSchemaProviderandIDatabaseSchemaProviderFactory. - Add a
<ProjectReference>to it fromSchemaDiscovery.Cli.csproj. - Register it (
builder.RegisterType<YourProviderFactory>().As<IDatabaseSchemaProviderFactory>()...) inSchemaDiscovery.Cli/DependencyResolution/DefaultModule.cs.
Logging
Logging uses Microsoft.Extensions.Logging with a console provider.
-v/--verbose switches the minimum level from Information to Debug,
which additionally logs each object as it's scanned and each file as it's
written. Errors (connection failures, unsupported providers, etc.) are
logged and the process exits with code 1.
| 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. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.27 | 114 | 9/4/2026 |
| 1.0.26 | 110 | 8/30/2026 |
| 1.0.25 | 120 | 8/26/2026 |
| 1.0.24 | 106 | 8/26/2026 |
| 1.0.23 | 121 | 8/25/2026 |
| 1.0.22 | 114 | 8/25/2026 |
| 1.0.21 | 111 | 8/25/2026 |
| 1.0.19 | 103 | 8/22/2026 |
| 1.0.18 | 110 | 8/21/2026 |
| 1.0.17 | 103 | 8/21/2026 |
| 1.0.15 | 104 | 8/21/2026 |
| 1.0.14 | 97 | 8/21/2026 |
| 1.0.13 | 97 | 8/21/2026 |
| 1.0.12 | 100 | 8/21/2026 |
| 1.0.11 | 118 | 8/20/2026 |
| 1.0.10 | 111 | 8/19/2026 |
| 1.0.9 | 100 | 8/19/2026 |
| 1.0.3 | 105 | 8/19/2026 |
| 1.0.2 | 103 | 8/19/2026 |
| 1.0.1 | 109 | 8/19/2026 |