Listing installed NuGet packages and their version from C# [duplicate] - c#

For context, I'm building an application that needs to download/unpack packages and their dependencies from arbitrary package sources (including the public gallery by default) and upgrade those packages to the latest version when requested. There are no project.json files or similar, it's all code driven. It's not a particularly complicated use case and didn't require too much code in the v2 APIs.
In v3 however, I can't figure out how to correctly interact with the local package store. For example, the FolderNuGetProject class that I would have thought lists all the packages on disk at a given location in FolderNuGetProject.GetInstalledPackagesAsync() just returns an empty enumerable. To make matters more confusing, FolderNuGetProject.PackageExists() actually does return whether the package exists on disk, which means GetInstalledPackagesAsync() and PackageExists() appear to be inconsistent.
None of the other NuGetProject derivatives appear related to the file system. Is there some other way of listing the packages that have been installed into a particular folder? If I need to create my own NuGetProject (and I'm hoping I don't), are there any methods that will help with parsing NuGet-generated folder names into package IDs and versions, or is the only reliable way of getting the ID and version to open the nuspec (and are there any easy to find methods for that)?
One interpretation of why this isn't working as I expect is that NuGetProject.GetInstalledPackagesAsync() isn't actually intended to get the installed packages (I.e., those that have been downloaded and unpacked), but rather those that have been declared in whatever project system is in use. For example, the BuildIntegratedNuGetProject class appears to return package references for the packages in the project.json, regardless of their status on disk. That would also explain why FolderNuGetProject just returns an empty enumerable, because there are no "declared" packages if you're just looking at the local repository.
TL;DR: What is the best way to crawl the local package store and get the packages and versions that are present there?
(this was also issue #2664 on the NuGet GitHub project, but was moved here by request)

Introduction
I have the same question and I looked your post on GitHub, Google and here. I try a lot of things to find the local packages.
I found some solutions, but I don't know if it's the best way to do it.
I posted a question about local packages too, because I can list all local packages, but I can't have the AssemblyReferences property (dll).
Code example
var rootPath = #"pathWhereNuGetPackagesAre";
var logger = new Logger();
List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>();
providers.AddRange(Repository.Provider.GetCoreV3());
FindLocalPackagesResourceV2 findLocalPackagev2 = new FindLocalPackagesResourceV2(rootPath);
var packageFound = findLocalPackagev2.GetPackages(logger, CancellationToken.None).FirstOrDefault();
//found, but missing a lot of informations...
var supportedFramework = new[] { ".NETFramework,Version=v4.6" };
var searchFilter = new SearchFilter(true)
{
SupportedFrameworks = supportedFramework,
IncludeDelisted = false
};
// The trick here is to put the local nuget path, not using the URL : https://api.nuget.org/v3/index.json
PackageSource localSource = new PackageSource(rootPath);
SourceRepository localRepository = new SourceRepository(localSource, providers);
PackageSearchResource searchLocalResource = await localRepository
.GetResourceAsync<PackageSearchResource>();
var packageFound3 = await searchLocalResource
.SearchAsync("Newtonsoft.Json", searchFilter, 0, 10, logger, CancellationToken.None);
var thePackage = packageFound3.FirstOrDefault();
// found but missing the assemblies property
public class Logger : ILogger
{
private List<string> logs = new List<string>();
public void LogDebug(string data)
{
logs.Add(data);
}
public void LogVerbose(string data)
{
logs.Add(data);
}
public void LogInformation(string data)
{
logs.Add(data);
}
public void LogMinimal(string data)
{
logs.Add(data);
}
public void LogWarning(string data)
{
logs.Add(data);
}
public void LogError(string data)
{
logs.Add(data);
}
public void LogInformationSummary(string data)
{
logs.Add(data);
}
public void LogErrorSummary(string data)
{
logs.Add(data);
}
}
Hope this will help!

In Visual Studio, Open package manager console.
List local (installed) packages use following command.
Get-Package
You can list all available packages on feed with following command
Get-Package -ListAvailable
If this commands are not working, check "Packege Manager Settings"->"Package Source" and confirm nuget feed configured correctly. (If you dont see your feed URL, You should add your feed there.) for details: Consume nuget package from VS
You can also check nuget feed configuration from this file
C:\Users{{user}}\AppData\Roaming\NuGet\NuGet.config
for more details about nuget config file: nuget config file
Also, Local nuget packages should ve stored at this path
C:\Users{{user}}.nuget\packages

Related

Method not found: 'System.Threading.Tasks.Task`1<Microsoft.Azure.Cosmos.Serialization.HybridRow.Result>

Getting below error when trying to insert into CosmosDb. Document is being inserted but throwing this error. I'm using .net core 5.0, Microsoft.Azure.Cosmos 3.17.0. Please advise.
Method not found: 'System.Threading.Tasks.Task1<Microsoft.Azure.Cosmos.Serialization.HybridRow.Result> Microsoft.Azure.Cosmos.Serialization.HybridRow.RecordIO.RecordIOStream.ReadRecordIOAsync(System.IO.Stream, System.Func2<System.ReadOnlyMemory1<Byte>,Microsoft.Azure.Cosmos.Serialization.HybridRow.Result>, System.Func2<System.ReadOnlyMemory1<Byte>,Microsoft.Azure.Cosmos.Serialization.HybridRow.Result>, Microsoft.Azure.Cosmos.Serialization.HybridRow.MemorySpanResizer1)'
protected virtual CosmosClient _cosmosClient
{
get
{
var options = new CosmosClientOptions
{
SerializerOptions = new CosmosSerializationOptions
{
IgnoreNullValues = true,
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
},
AllowBulkExecution = true
};
return new CosmosClient(_connectionString, options);
}
}
protected Container _cosmosContainer
{
get => _cosmosClient.GetContainer(_databaseId, _containerId);
}
protected async Task<T> CreateItemAsync<T>(string id, T model, PartitionKey partitionKey)
{
try
{
return await _cosmosContainer.CreateItemAsync(model, partitionKey);
}
catch (CosmosException ex)
{
LogCosmosDbError(ex);
throw;
}
}
Reference: https://github.com/Azure/azure-cosmos-dotnet-v3/issues/2292#issuecomment-796104041
Repeating what was stated in the comments for future reference: This is often related to DLL mismatching (older DLL for one or more dependencies).
It could be the project contains references to other projects with older versions of the SDK, in which case, make sure all the versions match.
It could also be an issue with your deployment process, maybe when the deployment happens, some DLLs are not getting overwritten, in which case, always make sure you are cleaning the /bin folder or updating all files with newer versions (some deployment processes might not update DLLs or the DLLs might be locked and currently in use).
I had the same issue with your same version, however, it turned out that the problem was caused by the dependencies installed in the CosmosDb Health Check from https://github.com/xabaril/AspNetCore.Diagnostics.HealthChecks I had version 5.03 of the health check and 3.17.1 of the Cosmos library and I was getting those errors.
When I removed the health check the issue went away.

How to identify Nuget metapackage?

I am working on tool where I need to programmatically identify Nuget package which is added as PackageReference in my current project is a metapackage or regular package. Since there is not enough documentation around Nuget V3 APIs, finding it hard to get those details. Any pointers in this regard are appreciated.
I tried following code and method GetMetadataAsync returns collection of NuGet.Protocol.PackageSearchMetadata type however there isn't any property on this type which says package is metapackage.
private async void button1_Click(object sender, EventArgs e)
{
ILogger log = new Logger();
List<Lazy<NuGet.Protocol.Core.Types.INuGetResourceProvider>> providers = new List<Lazy<NuGet.Protocol.Core.Types.INuGetResourceProvider>>();
providers.AddRange(Repository.Provider.GetCoreV3());
PackageSource packageSource = new PackageSource("https://api.nuget.org/v3/index.json");
SourceRepository sourceRepository = new SourceRepository(packageSource, providers);
PackageMetadataResource packageMetadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>();
IEnumerable<IPackageSearchMetadata> searchMetadata = await packageMetadataResource.GetMetadataAsync("Microsoft.AspNetCore.All", true, true,log, CancellationToken.None);
var list = searchMetadata.ToList();
}
A "metapackage" is more a convention for constructing a nuget package than an actual "type" / "property" on a package.
If a NuGet package doesn't contain any assets (libraries, content assets, tools, build logic etc.) - practically empty - but only has dependencies on other packages, it is referred to as "metapackage". The only way to test if a package fulfils this definition is to download and inspect its assets.

C#: use "Class Library" Project for plugins

this question follows my previous question.
I have a c# ASP.NET application and i want to provide support for plugins. Plugins can be custom c# classes, javascript, html, css, images, etc.
I see no problem as long as my application is extended with c# classes because all the user has to do is create a new "class library" project in visual studio and implement the interfaces, i provide. Then build a dll out of it and upload it to my server. The plugin-developer can add static files (html, js, css, etc.) into this project as well but i found some problems with that:
Every static file i add to the plugin project gets the build action "content" and it seems i cannot read those files from my server. (see my previously answered question). I have to manually select "Embedded Resource" on each file, so it is packed with the plugin dll.
I want to support Typescript for the plugins. The Typescript compiler generates javascript files in the same directory as the typescript-files. But the javascript files are not included in the project and therefore i have to include these in the plugin project and then set the correct build action. I don't want the plugin developers to do that all the time.
If the static files have the build action "enbedded resources", then the server can pickup these files by using the assembly.GetManifestResourceNames() method. This method returns the resources as a string. The path is not separated by \ or / but with a dot instead. So i am not able to distinguish between file path (this is relevant) or filename (also relevant to pickup the correct files), because the original filename can also have dots.
So i am starting to question the "class library" project type is right for my needs. Is there a way to get around of my issues or do i have to use another project type?
Thank you for any help!
Edit: Changed the question a little bit so it is better to understand.
You could make a zip package with the plugin dll and files. NuGet also uses this mechanism. A .nupkg is also just a zip file.
I would start by looking at MEF (Managed Extensibility Framework).
MSDN information can be found here: https://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx
From that link you can get more information and I believe there is a tutorial as well.
Oh, for me it seems very simple.
Let the developer create the plugin freestyle and put all the additional files in a directory, let's call it extras
To implement the your interface they will need your assembly so I guess you will ship it via nuget, or just some link. No matter what the case, provide them with some powershell script what will be required to run before the final build
The script would create zip archive from the extras directory and add it to the ClassLibrary project as EmbeddedResource.
As you mentioned earlier, you can access EmbeddedResource. So all you would do is to unpack it and you would have the exact directory tree.
The best idea would be to provide project template with script included, and also the empty zip archive added as embedded resource (it will be easier to just pack the files in the script and replace the file), and pre-build action set to run the script.
Am I missing something?
What about this.
In your web application, you could add a function that loop into your plugin directory and find DLL implementing an Iplugin (name is up to you) interface.
The interface is defined in a class library that both your web application and plugins have to implement.
You can use the Httpcontext Server mappath to read javascript and other files.
Here is a very basic implementation
First, you have the plugin interface (a class library implemented both by the web application and the individual plugins) I implemented sample properties and methods...
using System.Web;
public interface IPlugin
{
string Name { get; set; }
string Output { get; set; }
void Load(ref httpcontext Context);
void Dispose();
void Display();
}
Next, you have the Actual plugin class library we want to implement.
using System.Web;
using IPlugins;
public class AwesomePlugin : IPlugins.IPlugin
{
private string _Name = "AwesomePlugin";
private HttpContext _Context;
public string Name {
get { return _Name; }
set { _Name = value; }
}
public string Output {
get { return "Yay !!!"; }
set {
throw new NotImplementedException();
}
}
public void Display()
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Load(ref Web.HttpContext Context)
{
}
}
Finally, you dynamically load your plugins so you can use them in your application.
private Dictionary<string, IPlugins.IPlugin> _Plugins = new Dictionary<string, IPlugins.IPlugin>();
public void LoadPlugins()
{
lock (static_LoadPlugins_IpluginType_Init) {
try {
if (InitStaticVariableHelper(static_LoadPlugins_IpluginType_Init)) {
static_LoadPlugins_IpluginType = typeof(IPlugins.IPlugin);
}
} finally {
static_LoadPlugins_IpluginType_Init.State = 1;
}
}
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
dynamic Plugins = io.Directory.GetFiles(ServerPath);
foreach (string PluginPath in Plugins) {
dynamic Assembly = system.Reflection.Assembly.LoadFile(PluginPath);
Type PluginClass = Assembly.GetTypes.Where(T => T.GetInterface("IPlugin") != null).First;
IPlugins.IPlugin MyPlugin = Activator.CreateInstance(PluginClass);
MyPlugin.Load(httpcontext.Current);
_Plugins.#add(PluginClass.ToString, MyPlugin);
}
}
static bool InitStaticVariableHelper(Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag flag)
{
if (flag.State == 0) {
flag.State = 2;
return true;
} else if (flag.State == 2) {
throw new Microsoft.VisualBasic.CompilerServices.IncompleteInitialization();
} else {
return false;
}
}
That way, you can implement whatever you want in your plugin.
I believe you could load your plugins in a separate appdomain with restricted permissions to everything.
The files (Javascript / CSS / Html) should be available by accessing the full path of the file.
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
If the resources is embedded into the plugin DLL, you could read the stream from the loaded assembly or let the plugin manage its own embedded files.
For question Number 2, you can use
MS Build
to change the contenttype during build process.
You have to make yourself confident with MS Build

How to read the list of NuGet packages in packages.config programatically?

What's the best way to read (ideally via C#) the packages listed in packages.config files?
Within our source code repository I have a lot of solutions and projects and equally a lot of packages.config files. I'm trying to build a consolidated list of packages (and versions) in use across my source code repository.
I can see there is a NuGet.Core package available - how could I use this to achieve my goal?
Thanks
If you do not want to read the XML directly you can install the NuGet.Core NuGet package and then use the PackageReference class.
Here is some example code that uses this class to print out the package id and its version.
string fileName = #"c:\full\path\to\packages.config";
var file = new PackageReferenceFile(fileName);
foreach (PackageReference packageReference in file.GetPackageReferences())
{
Console.WriteLine("Id={0}, Version={1}", packageReference.Id, packageReference.Version);
}
You will need to find the packages.config files yourself which you can probably do with a directory search, something like:
foreach (string fileName in Directory.EnumerateFiles("d:\root\path", "packages.config", SearchOption.AllDirectories))
{
// Read the packages.config file...
}
An alternative and more up to date way of doing this is to install the NuGet.Packaging NuGet package and use code similar to:
var document = XDocument.Load (fileName);
var reader = new PackagesConfigReader (document);
foreach (PackageReference package in reader.GetPackages ())
{
Console.WriteLine (package.PackageIdentity);
}
As suggested you will need to install NuGet.Core, your solution may have several projects in it, so it's good to know how to specify the project name when installing. Let's say your Solution is MySolution and you have two projects Project01 & Project02 and you only want to install in Project02.
Install-Package NuGet.Core -ProjectName Project02
Next you will need to add a using statement in the whatever.cs page you are going to do your work to target the package and let's say you just want to get the version number so that you can print it out somewhere on your website. That is actually what I wanted to do.
using NuGet;
next I wanted to get at a specific package and read it's version number so that when we release my software I have a visual identifier at a certain place on my website that I can go to and see the version that is in production.
here is the code I wrote to populate a webforms label on my page.
protected void Page_Load(Object sender, EventArgs e)
{
var pkgRefpath = Server.MapPath("~/packages.config");
PackageReferenceFile nugetPkgConfig = new PackageReferenceFile(pkgRefpath);
IEnumerable<PackageReference> allPackages = nugetPkgConfig.GetPackageReferences();
var newtonsoftPkg = (
from pkg in allPackages
where pkg.Id == "Newtonsoft.Json"
select pkg
).FirstOrDefault();
if (newtonsoftPkg== null) return;
var newtonsoftPkg_Version = newtonsoftPkg.Version;
ltrNewtonsoftVer.Text = newtonsoftPkg_Version.ToString();
}
This is a slightly different answer to the question, but this shows the solution that I ended up with for my needs after finding this Question/Answer and modifying what I learned to suit my own needs. I hope it can help someone else out.

How to enforce same nuget package version across multiple c# projects?

I have a bunch of small C# projects which use a couple of NuGet packages. I'd like to be able to update version of a given package automatically. More then that: I'd like to be warned if a project uses different version from the others.
How do I enforce same version dependency across multiple C# projects?
As I haven't found another way to enforce this, I've written a unit test which will fail if different package versions are being found in any packages.config in any subfolder.
As this might be useful for others, you'll find the code below. You'll have to adapt the resolution of the root folder done in GetBackendDirectoryPath().
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using NUnit.Framework;
namespace Base.Test.Unit
{
[TestFixture]
public class NugetTest
{
private const string PACKAGES_CONFIG_FILE_NAME = "packages.config";
private const string BACKEND_DIRECTORY_NAME = "DeviceCloud/";
private const string PACKAGES_NODE_NAME = "packages";
private const string PACKAGE_ID_ATTRIBUTE_NAME = "id";
private const string PACKAGE_VERSION_ATTRIBUTE_NAME = "version";
/// <summary>
/// Tests that all referenced nuget packages have the same version by doing:
/// - Get all packages.config files contained in the backend
/// - Retrieve the id and version of all packages
/// - Fail this test if any referenced package has referenced to more than one version accross projects
/// - Output a message mentioning the different versions for each package
/// </summary>
[Test]
public void EnforceCoherentReferences()
{
// Act
IDictionary<string, ICollection<PackageVersionItem>> packageVersionsById = new Dictionary<string, ICollection<PackageVersionItem>>();
foreach (string packagesConfigFilePath in GetAllPackagesConfigFilePaths())
{
var doc = new XmlDocument();
doc.Load(packagesConfigFilePath);
XmlNode packagesNode = doc.SelectSingleNode(PACKAGES_NODE_NAME);
if (packagesNode != null && packagesNode.HasChildNodes)
{
foreach (var packageNode in packagesNode.ChildNodes.Cast<XmlNode>())
{
if (packageNode.Attributes == null)
{
continue;
}
string packageId = packageNode.Attributes[PACKAGE_ID_ATTRIBUTE_NAME].Value;
string packageVersion = packageNode.Attributes[PACKAGE_VERSION_ATTRIBUTE_NAME].Value;
if (!packageVersionsById.TryGetValue(packageId, out ICollection<PackageVersionItem> packageVersions))
{
packageVersions = new List<PackageVersionItem>();
packageVersionsById.Add(packageId, packageVersions);
}
//if (!packageVersions.Contains(packageVersion))
if(!packageVersions.Any(o=>o.Version.Equals(packageVersion)))
{
packageVersions.Add(new PackageVersionItem()
{
SourceFile = packagesConfigFilePath,
Version = packageVersion
});
}
if (packageVersions.Count > 1)
{
//breakpoint to examine package source
}
}
}
}
List<KeyValuePair<string, ICollection<PackageVersionItem>>> packagesWithIncoherentVersions = packageVersionsById.Where(kv => kv.Value.Count > 1).ToList();
// Assert
string errorMessage = string.Empty;
if (packagesWithIncoherentVersions.Any())
{
errorMessage = $"Some referenced packages have incoherent versions. Please fix them by adapting the nuget reference:{Environment.NewLine}";
foreach (var packagesWithIncoherentVersion in packagesWithIncoherentVersions)
{
string packageName = packagesWithIncoherentVersion.Key;
string packageVersions = string.Join("\n ", packagesWithIncoherentVersion.Value);
errorMessage += $"{packageName}:\n {packageVersions}\n\n";
}
}
Assert.IsTrue(packagesWithIncoherentVersions.Count == 0,errorMessage);
//Assert.IsEmpty(packagesWithIncoherentVersions, errorMessage);
}
private static IEnumerable<string> GetAllPackagesConfigFilePaths()
{
return Directory.GetFiles(GetBackendDirectoryPath(), PACKAGES_CONFIG_FILE_NAME, SearchOption.AllDirectories)
.Where(o=>!o.Contains(".nuget"));
}
private static string GetBackendDirectoryPath()
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
var uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path.Substring(0, path.IndexOf(BACKEND_DIRECTORY_NAME, StringComparison.Ordinal) + BACKEND_DIRECTORY_NAME.Length));
}
}
public class PackageVersionItem
{
public string SourceFile { get; set; }
public string Version { get; set; }
public override string ToString()
{
return $"{Version} in {SourceFile}";
}
}
}
I believe I have found a setup which solves this (and many other) problem(s).
I just realized one can use a folder as nuget source. Here is what I did:
root
+ localnuget
+ Newtonsoft.Json.6.0.1.nupkg
+ nuget.config
+ packages
+ Newtonsoft.Json.6.0.1
+ src
+ project1
nuget.config looks like this:
<configuration>
<config>
<add key="repositoryPath" value="packages" />
</config>
<packageSources>
<add key="local source" value="localnuget">
</packageSources>
</configuration>
You can add Nuget server to nuget.config to get access to updates or new dependencies during development time:
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
Once you're done, you can copy .nupkg from cache to localnuget folder to check it in.
There are 3 things I LOVE about this setup:
I'm now able to use Nuget features, such as adding props and targets. If you have a code generator (e.g. protobuf or thrift) this becomes pricesless.
It (partially) solves the problem of Visual Studio not copying all DLLs, because you need to specify dependencies in .nuspec file and nuget loads indirect dependencies automatically.
I used to have a single solution file for all projects so updating nuget packages was easier. I haven't tried yet but I think I solved that problem too. I can have nuget packages for the project I want to export from a given solution.
I don't know how to enforce it, but I've found the "Consolidate" tab to help.
This tab shows you packages that have different versions throughout the solution. From there you can select projects and use the install button to install the same package version across them. This tab can be found under "Manage NuGet for solution".
See Consolidate tab in Microsoft documentation.
Thank you for asking this - so I am not alone. I put considerable time into ensuring all projects in my solution use the same package version. The NuGet user interface (and also the command line interface) also contribues to having different versions among the projects within a solution. In particular when a new project is added to the solution and package X shall be added to the new project, NuGet is overly greedy to download the latest version from nuget.org instead of using the local version first, which would be the better default handling.
I completely agree with you, that NuGet should warn if different versions of a package are used within a solution. And it should help avoiding this and fixing such version maze.
The best I found to do now is to enumerate all packages.config files within the solution folder (your projects-root) which look like
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="6.0.6" targetFramework="net451" />
...
</packages>
then sorting the xml-nodes by id and analysing the version numbers.
If any package occurs with different version numbers, making them all equal and afterwards running the NuGet command
Update-Package -ProjectName 'acme.lab.project' -Reinstall
should fix wrong package versions.
(Since NuGet is open source it would certainly be a cool thing to get our hands dirty and implement the missing version-conflict avoidance utility.)
Additionally to the "Consolidate" tab in VS, you can use powershell Sync-Package
https://learn.microsoft.com/en-us/nuget/reference/ps-reference/ps-ref-sync-package.
Examples:
# Sync the Elmah package installed in the default project into the other projects in the solution
Sync-Package Elmah
# Sync the Elmah package installed in the ClassLibrary1 project into other projects in the solution
Sync-Package Elmah -ProjectName ClassLibrary1
# Sync Microsoft.Aspnet.package but not its dependencies into the other projects in the solution
Sync-Package Microsoft.Aspnet.Mvc -IgnoreDependencies
# Sync jQuery.Validation and install the highest version of jQuery (a dependency) from the package source
Sync-Package jQuery.Validation -DependencyVersion highest

Categories