I have a powershell script that I launch from Package Manager Console in Visual studio. In particular I have a question related to this command:
Update-Package -ProjectName $ProjectName -reinstall
This command installs packages from packages.config (I'm working mainly with legacy non sdk projects) into specified project.
All is good when I run this command for projects in the currently opened solution. But I don't want opening many solutions to update packages, instead I wrote a single script (called inside package manager in VS) that just goes into specific solution folder and update projects there. It works, but the generated paths to packages folder in .csproj file are created relative to the initially opened solution, not the solution where the particular project belongs to. Which leads to paths like:
..\..\..\..\SomeNotCurrentlyOpenedSolution\packages\..
instead just
..\packages\..
Is there a way to fix it? Maybe somehow it's possible to force VS thinking that a particular command is launched for specified solution, not always currently opened
UPDATE1: Please see commands log and description:
To illustrate the issue I have 2 newly created empty solutions: ClassLibrary1.sln and ClassLibrary2.sln. ClassLibrary1.csproj (a single project inside solution) has a reference on newtonsoft package. I've opened Package Manager Console in ClassLibrary2.sln and type the below commands:
PM> ls
Directory: C:\test\ClassLibrary2
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/9/2024 9:53 PM ClassLibrary2
-a---- 3/9/2024 9:53 PM 1145 ClassLibrary2.sln
PM> Get-Content ..\ClassLibrary1\ClassLibrary1\ClassLibrary1.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
...
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
...
As we can see, the ClassLibrary1.csproj has a reference on Newtonesoft.Json package with expected path.
As suggested by @jdweng, I've cded into ClassLibrary1.csproj.
PM> cd ..\ClassLibrary1\ClassLibrary1
PM> ls
Directory: C:\test\ClassLibrary1\ClassLibrary1
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/9/2024 9:28 PM bin
d----- 3/9/2024 9:28 PM obj
d----- 3/9/2024 9:28 PM Properties
-a---- 3/9/2024 9:28 PM 196 Class1.cs
-a---- 3/9/2024 9:30 PM 2528 ClassLibrary1.csproj
-a---- 3/9/2024 9:30 PM 144 packages.config
and try to update packages in ClassLibrary1.csproj. It doesn't work until I add a ClassLibrary1.csproj project into ClassLibrary2 solution (that's fine to me).
PM> Update-Package -ProjectName ClassLibrary1 -reinstall
Update-Package : Project 'ClassLibrary1' is not found.
At line:1 char:1
+ Update-Package -ProjectName ClassLibrary1 -reinstall
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (ClassLibrary1:String) [Update-Package], ItemNotFoundException
+ FullyQualifiedErrorId : NuGetProjectNotFound,NuGet.PackageManagement.PowerShellCmdlets.UpdatePackageCommand
after adding the ClassLibrary1.csproj project into the ClassLibrary2 solution, the command has succeded:
PM> ls
Directory: C:\test\ClassLibrary1\ClassLibrary1
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 3/9/2024 10:02 PM bin
d----- 3/9/2024 9:28 PM obj
d----- 3/9/2024 9:28 PM Properties
-a---- 3/9/2024 9:28 PM 196 Class1.cs
-a---- 3/9/2024 10:12 PM 2278 ClassLibrary1.csproj
-a---- 3/9/2024 10:12 PM 144 packages.config
PM> Update-Package -ProjectName ClassLibrary1 -reinstall
Attempting to gather dependency information for multiple packages with respect to project 'ClassLibrary1', targeting '.NETFramework,Version=v4.5.2'
Gathering dependency information took 216 ms
Attempting to resolve dependencies for multiple packages.
Resolving dependency information took 0 ms
Resolving actions install multiple packages
Retrieving package 'Newtonsoft.Json 13.0.3' from 'nuget.org'.
Removed package 'Newtonsoft.Json 13.0.3' from 'packages.config'
Successfully uninstalled 'Newtonsoft.Json 13.0.3' from ClassLibrary1
Package 'Newtonsoft.Json.13.0.3' already exists in folder 'C:\test\ClassLibrary2\packages'
Added package 'Newtonsoft.Json.13.0.3' to 'packages.config'
Successfully installed 'Newtonsoft.Json 13.0.3' to ClassLibrary1
Executing nuget actions took 718 ms
Time Elapsed: 00:00:00.9501830
PM> Get-Content .\ClassLibrary1.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\ClassLibrary2\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
you may see that the path to Newtonsoft.Json binaries has been changed. How can I prevent it?
For the context, what I want to do is to install packages mentioned in packages.config into the project file.
UPDATE2:
I tried approach suggested by @VonC related to nuget.exe binary:
nuget restore YourSolution.sln
or
nuget update YourProject.csproj
Unfortunatelly, it doesn't update neither a project file nor package.config. For now, using package manager console, I install a new package via:
Install-Package Newtonsoft.Json -Version 7.0.1
of if the package is already in packages.config run (yeah, it's weird case):
Update-Package -ProjectName $ProjectName -reinstall
As far as I can tell, nuget restore or nuget update does nothing with packages.config and project file
The
Update-Packagecommand in the Package Manager Console operates within the context of the currently loaded solution in Visual Studio. When you add a project from another solution and update its packages, NuGet resolves the paths based on the current solution's directory, which leads to the undesired path changes you have observed.Unfortunately, directly manipulating the solution context from within the Package Manager Console to trick Visual Studio into thinking a different solution is loaded for the scope of the command is not straightforward or supported by existing NuGet PowerShell commands.
That means you would have to consider, as an alternative, to automate package updates with the NuGet Command Line Interface (CLI) or MSBuild. Both tools can be used outside of Visual Studio and allow you to specify the solution or project files explicitly. That would give you more control over the process and avoids the context issue with Visual Studio's Package Manager Console.
For example, using the NuGet CLI, you can restore packages for a specific solution or project file without opening it in Visual Studio:
Or update packages for a project:
As a more straightforward workaround, after updating the packages, you could manually adjust the
HintPathin the.csprojfiles using a script. While not ideal, this would allow you to correct the paths without manually editing each file. You could write a PowerShell script to search and replace incorrect path segments in your project files.That script finds all
.csprojfiles, reads their content, replaces the incorrect relative path with the correct one, and saves the file.Marco Merola's comment expands on the last workaround, changing the
HintPathto use$(SolutionDir)instead of relative paths directly in your.csprojfiles: your project would reference the NuGet packages relative to the solution directory, regardless of where the project is being built from.For existing references in your
.csprojfiles, you would need to manually edit them or write a script to replace the relative paths with$(SolutionDir)packages\. For example, a<HintPath>that looks like..\..\packages\MyPackage\lib\net45\MyLib.dllwould be changed to$(SolutionDir)packages\MyPackage\lib\net45\MyLib.dll.The script mentioned in the blog post "Identifying Nuget package references which are using relative paths across whole solution" from yer.ac can be a starting point for automating the process of identifying and updating project references that do not use
$(SolutionDir).You can run this script for a specific solution directory by providing the path to the solution directory as an argument. It searches for
.csprojfiles, looks forHintPathentries without$(SolutionDir), and replaces them with paths that start with$(SolutionDir)packages\.To make this process even easier, you can incorporate the PowerShell script as an External Tool in Visual Studio, as described in the blog post. That allows you to run the script directly from Visual Studio for the currently opened solution, helping you make sure all project references use the
$(SolutionDir)variable for package paths.