Skip to main content

NuGetGallery.Core

Overview

NuGetGallery.Core is a shared class library that sits at the foundation of the NuGetGallery ecosystem. It targets both net472 and netstandard2.1, which allows it to be consumed by the ASP.NET MVC web application (net472) and by cross-platform .NET jobs and validators (netstandard2.1 / .NET 6+) without code duplication. The library provides:
  • The Entity Framework EntitiesContext (Gallery SQL Server database) and the IEntityRepository<T> / IReadOnlyEntityRepository<T> generic repository abstractions.
  • A complete Azure Blob Storage abstraction layer (ICloudBlobClient, ICloudBlobContainer, ISimpleCloudBlob, CloudBlobCoreFileStorageService) that allows tests and alternative environments to use file-system stubs.
  • Core service contracts and implementations for packages, symbol packages, license files, README files, and certificates that are needed by multiple processes (the web app, validation jobs, and tooling).
  • A structured auditing system (IAuditingService, AuditRecord subclasses, CloudAuditingService) that writes tamper-evident JSON audit records to blob storage.
  • Packaging helpers (PackageMetadata, ManifestValidator, PackageIdValidator, PackageStreamMetadata) for reading and validating .nupkg content server-side.
  • Cryptography utilities (CryptographyService) for hashing package streams (SHA-512).
  • Feature flag plumbing (IRevalidationStateService) and revalidation state management.
Several features are target-framework conditional. On net472, the project adds OData Microsoft.Data.Services.Client support and Azure SDK v11-era configuration helpers. On netstandard2.1, it pulls in Azure.Storage.Blobs (v12 SDK), Azure.Identity, and System.Text.Json. The two target frameworks compile the same source files unless #if guards or Condition attributes on ItemGroup nodes select different implementations.

Role in the NuGetGallery Ecosystem

Consumed by: NuGetGallery

The web application inherits EntitiesContext, all blob wrappers, ICorePackageService, ICoreFileStorageService, and the auditing infrastructure. The web-layer PackageFileService extends CorePackageFileService.

Consumed by: NuGetGallery.Services

The services layer builds on top of ICorePackageService and ICoreFileStorageService, extending them with higher-level upload, deprecation, rename, and vulnerability workflows.

Consumed by: Validation Jobs

The Validation Orchestrator and individual validators reference ICorePackageService, ICoreFileStorageService, and the blob abstractions on netstandard2.1 to manipulate package blobs without taking a dependency on the full web assembly.

Consumed by: CLI Tools

Tools such as GalleryTools, DatabaseMigrationTools, and RevalidateCertificate reference the Core library to access the Gallery database and blob storage with a shared, consistent abstraction.

Database Layer

EntitiesContext is the Entity Framework 6 DbContext for the Gallery SQL Server database. Key design points:
  • Migrations are permanently disabled via Database.SetInitializer<EntitiesContext>(null) in the static constructor. Schema changes are applied exclusively by DatabaseMigrationTools.
  • Read-only mode is enforced at the context level: SaveChanges / SaveChangesAsync throw ReadOnlyModeException when ReadOnly = true. All read-only replicas and diagnostics scenarios use ReadOnlyEntitiesContext.
  • GalleryDbContextFactory implements IDbContextFactory<EntitiesContext> so EF tooling can create an EntitiesContext instance without requiring a running application. The static GalleryEntitiesContextFactory delegate allows the web application to inject its own connection factory (e.g., using an MSI token).
  • Query hints are supported via IEntitiesContext.WithQueryHint(string) — a scope that injects SQL query hints (e.g., NOLOCK, index hints) through QueryHintInterceptor.
  • ObjectMaterializedInterceptingDbContext base class fires interception callbacks after EF materializes entities, enabling download count injection (DownloadCountObjectMaterializedInterceptor) without modifying entity types.

Key Files and Classes

File PathClass / InterfacePurpose
Entities/EntitiesContext.csEntitiesContextEF6 DbContext for the Gallery database. Exposes DbSet<T> properties for all entities; configures indexes and relationships via OnModelCreating.
Entities/IEntitiesContext.csIEntitiesContextInterface contract for the writable context; consumed by all services that need to persist changes.
Entities/IReadOnlyEntitiesContext.csIReadOnlyEntitiesContextRestricts callers to IQueryable<T> reads; used by the read replica path.
Entities/EntityRepository.csEntityRepository<T>Generic EF repository; wraps DbSet<T> with CommitChangesAsync, InsertOnCommit, DeleteOnCommit.
Services/CloudBlobCoreFileStorageService.csCloudBlobCoreFileStorageServiceCore Azure Blob implementation of ICoreFileStorageService. Lazily initializes containers, polls for cross-region copy completion (up to 10 min), generates SAS URIs, and handles If-None-Match / If-Match conditional requests.
Services/ICoreFileStorageService.csICoreFileStorageServicePrimary file storage abstraction: upload, download, delete, URI generation, SAS generation, server-side copy, conditional access.
Services/CorePackageFileService.csCorePackageFileServiceImplements ICorePackageFileService; manages .nupkg blob paths in the packages, validation, backup, and uploads containers using CoreConstants.Folders names.
Services/CorePackageService.csCorePackageServiceImplements ICorePackageService; handles UpdatePackageStreamMetadataAsync, UpdatePackageStatusAsync, UpdateIsLatestAsync, and certificate association (UpdatePackageSigningCertificateAsync).
Services/CoreSymbolPackageService.csCoreSymbolPackageServiceImplements ICoreSymbolPackageService; mirrors CorePackageService for .snupkg symbol packages.
Services/CoreLicenseFileService.csCoreLicenseFileServiceReads/writes license files from the flat container blob path used by V3 clients.
Services/CoreReadmeFileService.csCoreReadmeFileServiceReads/writes README files; shared between the web app (display) and validation jobs (storage).
Services/CryptographyService.csCryptographyServiceComputes SHA-512 hashes of package streams and generates random tokens for API keys and email confirmation.
Services/GalleryCloudBlobContainerInformationProvider.csGalleryCloudBlobContainerInformationProviderMaps CoreConstants.Folders.* folder name strings to Azure container names and their public/private access policy.
Services/RevalidationStateService.csRevalidationStateServiceReads and writes RevalidationState JSON from blob storage — used by revalidation jobs to checkpoint their progress.
Auditing/AuditingService.csAuditingService (abstract)Base class; serializes AuditRecord instances to JSON (with StringEnumConverter) and delegates to SaveAuditRecordAsync(string, ...).
Auditing/CloudAuditingService.csCloudAuditingServiceWrites audit records as timestamped blobs under {resourceType}/{path}/{action}/{timestamp}-{actor}.audit.v1.json in the auditing container.
Auditing/AggregateAuditingService.csAggregateAuditingServiceFan-outs audit writes to multiple IAuditingService instances (e.g., Cloud + local file in dev).
Auditing/PackageAuditRecord.csPackageAuditRecordCaptures package push, delete, unlist, relist, and reflow events with package ID, version, action, and actor.
Auditing/UserAuditRecord.csUserAuditRecordCaptures user registration, credential add/remove, password change, and account deletion events.
Packaging/PackageMetadata.csPackageMetadataParsed representation of a .nuspec; blocks server-side reserved metadata elements (created, packageHash, published, etc.) that must only be set by the V3 pipeline.
Packaging/ManifestValidator.csManifestValidatorValidates .nuspec well-formedness; checks for duplicate metadata elements, restricted elements, and schema version compatibility.
Packaging/PackageIdValidator.csPackageIdValidatorValidates package ID format (regex) and length; shared between the upload path and the push API.
Packaging/PackageStreamMetadata.csPackageStreamMetadataValue object carrying Hash, HashAlgorithm, and Size — computed from the raw package stream and stored on the Package entity.
CoreConstants.csCoreConstantsCanonical string constants for blob folder names, file extensions, content types, and HTTP header names used across all consuming projects.

Dependencies

Internal Project References (net472 only)

ProjectRole
NuGet.Services.EntitiesEF entity types (Package, PackageRegistration, User, Certificate, SymbolPackage, etc.) consumed by EntitiesContext
NuGet.Services.FeatureFlagsIFeatureFlagService interface and blob-backed implementation (net472 path)
NuGet.Services.Messaging.EmailEmail message contracts referenced by auditing records on net472
NuGet.Services.ValidationPackageValidationSet, PackageValidation, and validation status enums
NuGet.Services.Validation.IssuesTyped validation issue classes referenced by audit records

Key NuGet Package Dependencies

PackageTargetPurpose
Azure.Storage.Blobsnetstandard2.1Azure Blob Storage SDK v12 (BlobServiceClient, BlobContainerClient, BlobClient)
Azure.Core / Azure.Identitynetstandard2.1TokenCredential and managed identity support for blob auth
EntityFrameworkbothEF6 ORM for Gallery SQL Server database
NuGet.PackagingbothPackageArchiveReader, NuspecReader for reading .nupkg content
Microsoft.Extensions.Caching.Memorynetstandard2.1IMemoryCache used by the typosquatting cache service helper
System.Text.Jsonnetstandard2.1JSON serialization for revalidation state and feature flag blobs
System.Formats.Asn1netstandard2.1ASN.1 parsing for certificate thumbprint handling
Microsoft.Data.Services.Clientnet472OData client for legacy data service integration
Microsoft.WindowsAzure.ConfigurationManagernet472Legacy Azure SDK configuration helpers

Notable Patterns and Implementation Details

Lazy container initialization. CloudBlobCoreFileStorageService uses a ConcurrentDictionary<string, ICloudBlobContainer> to cache initialized container references. The first request to any folder name triggers GetContainerAsync, which creates the container if _initializeContainer=true. Subsequent calls return the cached reference without a round-trip.
Server-side blob copy with polling. CopyFileAsync uses the Azure blob server-side copy API and polls at 500 ms intervals for up to 10 minutes (MaxCopyDuration). This pattern is used when promoting a validated package from the validation container to the public packages container without streaming bytes through the application server.
EntitiesContext migrations are permanently off. The static constructor calls Database.SetInitializer<EntitiesContext>(null). Any developer who inadvertently calls Database.Migrate() or enables CreateDatabaseIfNotExists will get a runtime exception or, worse, no-op silently. All DDL changes must go through DatabaseMigrationTools.
Conditional target-framework compilation. The .csproj uses Condition="'$(TargetFramework)' == 'net472'" and Condition="'$(TargetFramework)' == 'netstandard2.1'" on ItemGroup nodes rather than #if preprocessor guards for most of the TFM-specific differences. This keeps source files clean but means the project’s dependency graph differs between the two compilation targets.
Auditing is fire-and-forget safe. AggregateAuditingService catches and logs exceptions from individual IAuditingService implementations so that an audit write failure never bubbles up to the caller and does not roll back a business operation. Audit records are best-effort; correctness of the package operation takes priority.
PackageMetadata blocks reserved nuspec elements. On upload, PackageMetadata strips any element whose name appears in RestrictedMetadataElements (e.g., created, packageHash, published, verbatimVersion). These fields are computed server-side by the V3 catalog pipeline; allowing authors to inject them would corrupt the catalog feed.