Skip to main content

Overview

NuGet.Services.Validation is a shared library that sits at the center of NuGet Gallery’s asynchronous validation pipeline. It provides:
  • Entity Framework entities and DbContext (ValidationEntitiesContext) — the canonical data model for all validation state stored in the dedicated Validation SQL database.
  • Service Bus messaging contracts — serialization/deserialization of the four message types that drive the validation workflow (StartValidation, ProcessValidationSet, CheckValidationSet, CheckValidator).
  • EF6 code-first migrations — 20+ incremental migrations that have evolved the schema from 2017 through 2024, covering signing, scanning, revalidation, symbols, and content-scan features.
  • Cross-cutting enums and data objects shared between the Gallery front-end (NuGetGallery) and the back-end Orchestrator / validator workers.
The library targets both net472 (for legacy IIS-hosted services) and netstandard2.1 (for newer worker roles).
The ValidationEntitiesContext static constructor explicitly calls Database.SetInitializer<ValidationEntitiesContext>(null), disabling automatic EF migrations at runtime. All schema changes must be applied deliberately via the Migrations classes or a dedicated migration tool.

NuGet Gallery (front-end)
        │  enqueues via IPackageValidationEnqueuer

Azure Service Bus Topic
        │  consumed by

Validation Orchestrator (NuGet.Services.Validation.Orchestrator)
        │  reads/writes via IValidationEntitiesContext

Validation SQL Database   ←──  this library owns the schema

        ├── PackageValidationSets / PackageValidations
        ├── signature.* tables  (signing, certs, timestamps)
        ├── scan.*  tables      (malware scan operations)
        └── ContentScanOperationState (CVS content scanning)
This project is not an executable — it is a shared assembly consumed by:
  • NuGetGallery — to enqueue validation requests and read signing state.
  • NuGet.Services.Validation.Orchestrator — to orchestrate individual validators and persist their results.
  • Individual validator workers — which write back ValidatorStatus rows to communicate progress.

Key Files and Classes

File PathClass / InterfacePurpose
Entities/ValidationEntitiesContext.csValidationEntitiesContextEF6 DbContext; defines all IDbSet<T> properties and fluent model configuration across 5 schema groups.
Entities/IValidationContext.csIValidationEntitiesContextInterface abstraction over ValidationEntitiesContext; used for DI and testability.
Entities/PackageValidationSet.csPackageValidationSetRoot aggregate representing one round of validation for a package; identified by ValidationTrackingId (GUID).
Entities/PackageValidation.csPackageValidationA single named validation step (e.g., "PackageSigning") within a set; tracks status and timing.
Entities/ValidatorStatus.csValidatorStatusCross-service handshake record written by the Orchestrator and updated by downstream validator workers.
Entities/PackageSigningState.csPackageSigningStateAggregate signing status for a package; lives in the signature schema.
Entities/EndCertificate.csEndCertificateRepresents a signing or timestamping leaf certificate identified by SHA-256 thumbprint.
Entities/ContentScanOperationState.csContentScanOperationStateTracks CVS (Content Vulnerability Scanning) job state per file path within a package.
Entities/PackageRevalidation.csPackageRevalidationQueued entry for re-running validation against already-published packages.
Entities/BaseValidationIssue.csBaseValidationIssueAbstract base for PackageValidationIssue and ValidatorIssue; holds IssueCode + serialized Data.
IPackageValidationEnqueuer.csIPackageValidationEnqueuerInterface for sending validation messages to Service Bus, with optional scheduled delivery.
PackageValidationEnqueuer.csPackageValidationEnqueuerConcrete implementation; delegates to ITopicClient and IServiceBusMessageSerializer.
ServiceBusMessageSerializer.csServiceBusMessageSerializerSerializes/deserializes all four PackageValidationMessageType variants using versioned [Schema] attributes.
PackageValidationMessageData.csPackageValidationMessageDataDiscriminated-union message envelope; exposes exactly one non-null payload property per message type.
ValidatingType.csValidatingType (enum)Distinguishes Package, SymbolPackage, and Generic validation targets.
Migrations/ValidationMigrationsConfiguration.csValidationMigrationsConfigurationEF DbMigrationsConfiguration; enables ordered migration application.

Dependencies

NuGet Package References

PackagePurpose
EntityFrameworkEF6 ORM; DbContext, DbModelBuilder, migrations
NuGet.VersioningNuGet version parsing/normalization used in entity comparisons
Azure.CoreAzure SDK primitives (used transitively)
System.Text.JsonJSON serialization support alongside Newtonsoft

Internal Project References

ProjectRole
NuGet.Services.ContractsShared enums and interfaces (ValidationIssueCode, ValidationStatus, etc.)
NuGet.Services.ServiceBusITopicClient, IBrokeredMessage, BrokeredMessageSerializer<T>, [Schema] attribute

Notable Patterns and Implementation Details

Versioned Service Bus Schemas

Each message type is serialized using a private inner class annotated with [Schema(Name=..., Version=1)]. The deserializer switches on the schema name string read from the brokered message header, making it straightforward to add Version=2 classes later without breaking existing consumers.

Discriminated-Union Message Envelope

PackageValidationMessageData enforces exactly one non-null payload out of four possible data slots at construction time. Passing more than one non-null payload throws ArgumentException, preventing message type mismatches silently.

Multi-Schema SQL Layout

Signing entities live in the signature schema; scan entities in the scan schema; everything else in dbo. This makes permission scoping and backup partitioning cleaner at the SQL Server level.

Dual-Target Library

The project targets both net472 and netstandard2.1. EF6 is Windows/full-framework-only for actual DB access, but the entity POCOs and message contracts compile cleanly under netstandard2.1 for use in lightweight cross-platform services.
The BatchId column on ValidatorStatus is intentionally ignored in the base ValidationEntitiesContext. Derived contexts (used in other internal services that have the column) override ConfigureBatchIdProperty() to enable it. Forgetting to override in a derived context will silently drop the column from EF’s model.
Certificate thumbprints are stored as varchar (not nvarchar) and sized at 256 characters — large enough to accommodate future hash algorithms beyond SHA-256 without requiring a schema migration.
ValidationDbContextFactory supports three construction modes: a static delegate (ValidationEntitiesContextFactory), a direct connection string, or the default "Validation.SqlServer" named connection. The static delegate is useful for injecting AAD token-based DbConnection objects in Azure-hosted migration tools.

Message Flow Summary

// 1. Gallery enqueues a new validation
var message = PackageValidationMessageData.NewStartValidation(
    trackingId, contentType, contentUrl, properties);
await enqueuer.SendMessageAsync(message);

// 2. Orchestrator processes the set
var process = PackageValidationMessageData.NewProcessValidationSet(
    packageId, version, trackingId, ValidatingType.Package, entityKey);

// 3. Orchestrator checks progress
var check = PackageValidationMessageData.NewCheckValidationSet(trackingId, extendExpiration: false);

// 4. Orchestrator checks a specific validator step
var checkValidator = PackageValidationMessageData.NewCheckValidator(validationId);