Exploring the NuGet client libraries

 
 
  • Gérald Barré

We often use NuGet through Visual Studio or dotnet CLI, but you can also use the same using your own application. In this post, I'm going to show multiple examples of using the .NET client library for NuGet.

To start using the NuGet client library, you need to add a reference to the NuGet packages:

csproj (MSBuild project file)
  <ItemGroup>
    <PackageReference Include="NuGet.Protocol" Version="5.10.0" />
    <PackageReference Include="NuGet.LibraryModel" Version="5.10.0" />
    <PackageReference Include="NuGet.Commands" Version="5.10.0" />
    <PackageReference Include="NuGet.Resolver" Version="5.10.0" />
  </ItemGroup>

#Reading user settings

C#
// Load machine and user settings
var settings = Settings.LoadDefaultSettings(null);

// Extract some data from the settings
string globalPackagesFolder = SettingsUtility.GetGlobalPackagesFolder(settings);
var sources = SettingsUtility.GetEnabledSources(settings);

#Searching packages

C#
var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");
var resource = await repository.GetResourceAsync<PackageSearchResource>();
var searchFilter = new SearchFilter(includePrerelease: false)
{
    IncludeDelisted = false,
    SupportedFrameworks = new[] { FrameworkConstants.CommonFrameworks.Net50.Framework },
};

var results = await resource.SearchAsync(
    "Meziantou.Framework",
    searchFilter,
    skip: 0,
    take: 10,
    NullLogger.Instance,
    CancellationToken.None);

foreach (IPackageSearchMetadata result in results)
{
    Console.WriteLine($"{result.Identity.Id}@{result.Identity.Version}");
}

#Listing package versions

C#
var cache = new SourceCacheContext();
var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");

var resource = await repository.GetResourceAsync<FindPackageByIdResource>();
var versions = await resource.GetAllVersionsAsync(
    "Meziantou.Framework",
    cache,
    NullLogger.Instance,
    CancellationToken.None);

Console.WriteLine(versions.FindBestMatch(VersionRange.Parse("3.0.*"), version => version));

If you only want the listed versions:

C#
var cache = new SourceCacheContext();
var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");

var resource = await repository.GetResourceAsync<PackageMetadataResource>();

var metadata = await resource.GetMetadataAsync(
    "Meziantou.Framework",
    includePrerelease: true,
    includeUnlisted: false,
    cache,
    NullLogger.Instance,
    CancellationToken.None);

foreach (var version in metadata.Select(m => m.Identity.Version))
{
    Console.WriteLine(version);
}

#Downloading a package

C#
var cache = new SourceCacheContext();
var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");
var resource = await repository.GetResourceAsync<FindPackageByIdResource>();

using MemoryStream packageStream = new MemoryStream();
await resource.CopyNupkgToStreamAsync(
    "Meziantou.Framework",
    new NuGetVersion("3.0.15"),
    packageStream,
    cache,
    NullLogger.Instance,
    CancellationToken.None);

#Resolving all dependencies

C#
var packageId = "Microsoft.Extensions.Logging";
var packageVersion = "7.0.0";
var targetFramework = "net6.0";

var cache = new SourceCacheContext();
var repositories = new[] { Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json") };
var logger = NullLogger.Instance;
var cancellationToken = CancellationToken.None;

// Find all potential dependencies
var packages = new HashSet<SourcePackageDependencyInfo>(PackageIdentityComparer.Default);
await ListAllPackageDependencies(
    new PackageIdentity(packageId, NuGetVersion.Parse(packageVersion)),
    repositories,
    NuGetFramework.ParseFolder(targetFramework),
    cache,
    logger,
    packages,
    cancellationToken);

// Find the best version for each package
var resolverContext = new PackageResolverContext(
    dependencyBehavior: DependencyBehavior.Lowest,
    targetIds: new[] { packageId },
    requiredPackageIds: Enumerable.Empty<string>(),
    packagesConfig: Enumerable.Empty<PackageReference>(),
    preferredVersions: Enumerable.Empty<PackageIdentity>(),
    availablePackages: packages,
    repositories.Select(r => r.PackageSource),
    NullLogger.Instance);

var resolver = new PackageResolver();
var resolvedPackages = resolver.Resolve(resolverContext, CancellationToken.None);

foreach (var resolvedPackage in resolvedPackages)
{
    Console.WriteLine($"{resolvedPackage.Id}@{resolvedPackage.Version}");
}

static async Task ListAllPackageDependencies(
    PackageIdentity package,
    IEnumerable<SourceRepository> repositories,
    NuGetFramework framework,
    SourceCacheContext cache,
    ILogger logger,
    HashSet<SourcePackageDependencyInfo> dependencies,
    CancellationToken cancellationToken)
{
    if (dependencies.Contains(package))
        return;

    foreach (var repository in repositories)
    {
        var dependencyInfoResource = await repository.GetResourceAsync<DependencyInfoResource>();
        var dependencyInfo = await dependencyInfoResource.ResolvePackage(package, framework, cache, logger, cancellationToken);

        if (dependencyInfo == null)
            continue;

        if (dependencies.Add(dependencyInfo))
        {
            foreach (var dependency in dependencyInfo.Dependencies)
            {
                await ListAllPackageDependencies(
                    new PackageIdentity(dependency.Id, dependency.VersionRange.MinVersion),
                    repositories,
                    framework,
                    cache,
                    logger,
                    dependencies,
                    cancellationToken);
            }
        }
    }
}

#Getting the package download URL

C#
var cache = new SourceCacheContext();
var repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");
var resource = await repository.GetResourceAsync<DependencyInfoResource>();

var identity = new NuGet.Packaging.Core.PackageIdentity("Meziantou.Framework", new NuGetVersion("3.0.15"));
var package = await resource.ResolvePackage(identity, NuGetFramework.AnyFramework, cache, NullLogger.Instance, CancellationToken.None);
_ = package.DownloadUri;

#Extracting package metadata

C#
// packageStream comes from the previous example
using var packageReader = new PackageArchiveReader(packageStream);
var nuspecReader = await packageReader.GetNuspecReaderAsync(cancellationToken);

_ = nuspecReader.GetDescription();
_ = nuspecReader.GetLicenseMetadata().License;

// List dependencies
foreach (var dependencyGroup in nuspecReader.GetDependencyGroups())
{
    _ = dependencyGroup.TargetFramework;
    _ = dependencyGroup.Packages;
}

#Checking package compatibility

C#
// true: a netstandard2.0 library is compatible with net5.0 project
DefaultCompatibilityProvider.Instance.IsCompatible(
    NuGetFramework.Parse("net5.0"),
    NuGetFramework.Parse("netstandard2.0"));

// false: a net5.0 library is not compatible with netstandard2.0 project
DefaultCompatibilityProvider.Instance.IsCompatible(
    NuGetFramework.Parse("netstandard2.0"),
    NuGetFramework.Parse("net5.0"));

#Installing package to the global packages folder

C#
ILogger logger = NullLogger.Instance;
CancellationToken cancellationToken = CancellationToken.None;

var settings = Settings.LoadDefaultSettings(null);
var globalPackagesFolder = SettingsUtility.GetGlobalPackagesFolder(settings);
var source = "https://api.nuget.org/v3/index.json";

var cache = new SourceCacheContext();
var repository = Repository.Factory.GetCoreV3(source);
var resource = await repository.GetResourceAsync<FindPackageByIdResource>();

var packageId = "Meziantou.Framework";
NuGetVersion version = new NuGetVersion("3.0.15");

// Download the package
using MemoryStream packageStream = new MemoryStream();
await resource.CopyNupkgToStreamAsync(
    packageId,
    version,
    packageStream,
    cache,
    logger,
    cancellationToken);

packageStream.Seek(0, SeekOrigin.Begin);

// Add it to the global package folder
var downloadResult = await GlobalPackagesFolderUtility.AddPackageAsync(
    source,
    new PackageIdentity(packageId, version),
    packageStream,
    globalPackagesFolder,
    parentId: Guid.Empty,
    ClientPolicyContext.GetClientPolicy(settings, logger),
    logger,
    cancellationToken);

#Retrieving packages from the global packages folder

C#
var settings = Settings.LoadDefaultSettings(null);
var globalPackagesFolder = SettingsUtility.GetGlobalPackagesFolder(settings);

var packageId = "Meziantou.Framework";
NuGetVersion version = new NuGetVersion("3.0.15");
var package = GlobalPackagesFolderUtility.GetPackage(new PackageIdentity(packageId, version), globalPackagesFolder);
if (package != null)
{
    _ = package.PackageReader.NuspecReader;
    _ = package.PackageReader.GetSupportedFrameworks();
}

#List packages from nuget.org

C#
private static async Task DownloadCatalog()
{
    var packagesPublishedAfter = DateTime.UtcNow.AddHours(-1);

    var sourceRepository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");
    var serviceIndex = await sourceRepository.GetResourceAsync<ServiceIndexResourceV3>();
    var catalogIndexUrl = serviceIndex.GetServiceEntryUri("Catalog/3.0.0");

    using var httpClient = new HttpClient();
    var index = await httpClient.GetFromJsonAsync<CatalogIndex>(catalogIndexUrl);
    var pageItems = index.Items.Where(x => x.CommitTimeStamp > packagesPublishedAfter);

    var leaves = new List<CatalogLeaf>();
    foreach (var pageItem in pageItems)
    {
        var page = await httpClient.GetFromJsonAsync<CatalogPage>(pageItem.Url);
        leaves.AddRange(page.Items.Where(x => x.CommitTimeStamp > packagesPublishedAfter));
    }

    foreach (var leaf in leaves.OrderBy(x => x.CommitTimeStamp))
    {
        Console.WriteLine($"{leaf.Id}@{leaf.Version}");

        // Get the full data about the leaf item
        var str = await httpClient.GetStringAsync(leaf.Url);
    }
}

public abstract class CatalogEntity
{
    [JsonPropertyName("@id")]
    public string Url { get; set; }

    [JsonPropertyName("commitTimeStamp")]
    public DateTime CommitTimeStamp { get; set; }
}

public sealed class CatalogIndex : CatalogEntity
{
    [JsonPropertyName("items")]
    public List<CatalogPage> Items { get; set; }
}

public sealed class CatalogPage : CatalogEntity
{
    [JsonPropertyName("items")]
    public List<CatalogLeaf> Items { get; set; }
}

public sealed class CatalogLeaf : CatalogEntity
{
    [JsonPropertyName("nuget:id")]
    public string Id { get; set; }

    [JsonPropertyName("nuget:version")]
    public string Version { get; set; }

    [JsonPropertyName("@type")]
    public string Type { get; set; }
}

#Pushing packages

C#
ILogger logger = NullLogger.Instance;
CancellationToken cancellationToken = CancellationToken.None;

string apiKey = "my-api-key";
var packageSource = new PackageSource("https://api.nuget.org/v3/index.json");
SourceRepository repository = Repository.Factory.GetCoreV3(packageSource);
PackageUpdateResource resource = await repository.GetResourceAsync<PackageUpdateResource>();
await resource.Push(
    new[] { "package.1.0.0.nupkg", "package.1.0.0.snupkg" },
    symbolSource: null,
    timeoutInSecond: 60,
    disableBuffering: false,
    getApiKey: packageSource => apiKey,
    getSymbolApiKey: packageSource => null,
    noServiceEndpoint: false,
    skipDuplicate: true,
    symbolPackageUpdateResource: null,
    logger);

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub