Skip to main content

Overview

DatabaseMigrationTools is a .NET Framework 4.7.2 console application (NuGet job) that runs Entity Framework Code First database migrations against the four core SQL Server databases used by NuGet.org:
  • GalleryDatabase — the primary NuGet package index store
  • SupportRequestDatabase — admin support ticket data
  • ValidationDatabase — package signing / validation pipeline state
  • CatalogValidationDatabase — catalog consistency validation state
The tool is invoked as part of the deployment pipeline. It accepts a single MigrationTargetDatabase argument that selects which database to migrate. A separate NuGet package is published for each target (four .nuspec files), allowing deployment pipelines to trigger migrations independently per database.
The tool adds a hard 30-second Thread.Sleep after the job completes. This is a documented workaround for an Application Insights SDK bug where TelemetryChannel.Flush() is unreliable.

Role in System

Deployment Gate

Runs as a post-deploy step (via Scripts/PostDeploy.ps1 → RunJob.cmd) before traffic is shifted to a new deployment slot, ensuring the schema is always ahead of the application code.

Multi-Database Fan-Out

A single compiled binary covers all four databases. The MigrationTargetDatabase argument selects the context at runtime, so four separate NuGet packages can be published from one build artifact.

Entra ID–Aware

All *DbMigrationContext classes capture the SQL connection’s AccessToken at construction time and restore it before every EF context creation via SetSqlConnectionAccessToken(), supporting MSI / service principal authentication.

Migration Safety Check

Before applying any pending migration, Job.CheckIsValidMigration() compares the ordered list of applied database migrations against the local code migrations to detect history divergence.

Key Files and Classes

FileClass / TypePurpose
Program.csProgramEntry point. Constructs MigrationContextFactory, wires it into Job, runs via JobRunner.RunOnce, then sleeps 30s for telemetry flush.
MigrationContextFactory.csMigrationContextFactoryRegistry mapping each MigrationTargetDatabaseArgumentNames constant to an async factory delegate.
GalleryDbMigrationContext.csGalleryDbMigrationContextConfigures GalleryDbContextFactory and MigrationsConfiguration for the main NuGet Gallery database.
SupportRequestDbMigrationContext.csSupportRequestDbMigrationContextConfigures SupportRequestDbContextFactory for the admin support-request database.
ValidationDbMigrationContext.csValidationDbMigrationContextConfigures ValidationDbContextFactory for the package-validation pipeline database.
CatalogValidationDbMigrationContext.csCatalogValidationDbMigrationContextConfigures CatalogValidationDbContextFactory for the catalog-validation database.
Scripts/PostDeploy.ps1Thin PowerShell wrapper invoked by the deployment system; calls RunJob.cmd and throws on non-zero exit code.
DatabaseMigration.*.nuspec (×4)Per-database NuGet packaging descriptors.
Classes in the companion library NuGet.Services.DatabaseMigration:
FileClass / InterfacePurpose
IMigrationContext.csIMigrationContextContract: exposes SqlConnection, SqlConnectionAccessToken, and GetDbMigrator factory.
BaseDbMigrationContext.csBaseDbMigrationContextBase implementation; holds the connection, restores access token when connection is closed.
Job.csJobCore job logic: reads the MigrationTargetDatabase arg, checks migration validity, scripts pending SQL, then calls migrator.Update().
MigrationTargetDatabaseArgumentNames.csMigrationTargetDatabaseArgumentNamesString constants (GalleryDatabase, SupportRequestDatabase, ValidationDatabase, CatalogValidationDatabase).

Dependencies

NuGet Packages

PackagePurpose
EntityFrameworkEF6 DbMigrator, DbMigrationsConfiguration, MigratorScriptingDecorator — the migration engine.

Internal Project References

ProjectWhat It Provides
NuGet.Services.DatabaseMigrationJob, BaseDbMigrationContext, IMigrationContext, core migration abstractions.
NuGetGalleryEntitiesContext, MigrationsConfiguration, GalleryDbContextFactory.
NuGet.Services.ValidationValidationEntitiesContext, ValidationMigrationsConfiguration.
NuGet.Services.CatalogValidationCatalogValidationEntitiesContext, CatalogValidationMigrationsConfiguration.

Deployment Flow

Deploy pipeline
  └─ Install NuGet package (e.g. DatabaseMigration.Gallery)
       └─ Scripts/PostDeploy.ps1
            └─ RunJob.cmd  →  DatabaseMigrationTools.exe
                 --MigrationTargetDatabase GalleryDatabase
                 --Configuration <key-vault-or-json-path>

Notable Patterns and Quirks

Static context factory pattern. Each *DbMigrationContext constructor assigns a lambda to a static *ContextFactory property. This side-effectful static mutation works for a single-run console job but would break under concurrent or parallel migration runs in the same process.
Access token renewal gap. SetSqlConnectionAccessToken() only resets the token when SqlConnection.State == ConnectionState.Closed. If a token expires mid-migration while the connection is open, the migration will fail. Long-running schema changes on databases with short token lifetimes are a risk.
Skipped Gallery migration. Job.cs hard-codes SkipGalleryDatabaseMigrationFile = "201304262245205_CuratedPackagesUniqueIndex". This migration exists in the database history but has no corresponding local file; the validation loop skips it to avoid a spurious divergence error.
Migration script logging. Before calling migrator.Update(), the job uses MigratorScriptingDecorator to generate and log the full SQL for all pending migrations — making the deployment log an audit trail of every DDL statement applied.
Validation check is partially disabled. CheckIsValidMigration contains commented-out guards that would throw when either the database or local migration list is empty. Empty-list scenarios are currently silently allowed.