NuGet Package Restore for Projects in Multiple Solutions

May 27th 2013 NuGet Visual Studio

If you've only been using NuGet in typical scenarios with a single solution containing a couple of projects, it probably worked great for you. As soon as the project structure is a bit more complex, things start falling apart. There are two main reasons why it doesn't work as expected when individual projects are included in multiple solution files residing in different folders:

  • By default packages are downloaded to a subfolder inside the same folder as the solution file. Projects reference libraries inside this subfolder using a relative hint path. If not all solution files are in the same folder Visual Studio can't find the libraries when the project is open from a different solution than when the library was originally added. To make this work the libraries would need to be in multiple folders.
  • Package restore feature also adds a .targets file and a couple of supporting files to a subfolder inside the same folder as the solution file. Since the .targets file actually implements this functionality, package restore doesn't work if the file is not in the expected location. Until recently the projects even failed to load in such cases making it impossible to open them without a solution or when added to a solution without package restore enabled.

For a long time this has a been a limiting factor for introducing NuGet to the development process in the company I work for. Trying to use it often caused problems for other developers, until we have finally found a permanent solution based on Eddie Garmon's post to a discussion thread on NuGet's CodePlex site.

The first part of the solution takes advantage of hierarchical NuGet.config file support introduced in NuGet 2.1. It should be put in the repository root (trunk in Subversion or master in Git terminology) containing the following settings:

<configuration>
  <config>
    <!-- Defines a common package repository for all solutions -->
    <add key="repositoryPath" value=".\Packages" />
  </config>
</configuration>

As long as you don't have other NuGet.config files in your project directory structure overriding the setting, this should make sure that when installing NuGet packages to any project in your source code repository, they will be put to a common Packages repository at the root level. Libraries will also always be referenced from there preventing the build from failing because Visual Studio can't locate them.

The second problem is a bit harder to crack and involves a couple of steps:

  • First temporarily enable NuGet package restore for a single solution from within Visual Studio to create the .nuget folder in the same folder as the selected solution.
  • Remove the .nuget solution folder from inside Visual Studio
  • Move this file system folder to the root of your source control repository where you have already put your NuGet.config file.
  • Delete the NuGet.config file inside this .nuget folder.
  • Open and modify a single line in NuGet.targets file inside .nuget folder:

    <!-- Line before the change -->
    <NuGetExePath Condition=" '$(NuGetExePath)' == '' ">
        $(NuGetToolsPath)\NuGet.exe
    </NuGetExePath>
    <!-- Line after the change -->
    <NuGetExePath Condition=" '$(NuGetExePath)' == '' ">
        $(MSBuildThisFileDirectory)\NuGet.exe
    </NuGetExePath>
    
  • Edit all project files inside the previously selected solution (you need to unload a project before the edit command on its file becomes available in Visual Studio). Again you only need to modify a single line pointing to the NuGet.targets file:

    <!-- Line before the change -->
    <Import Project="$(SolutionDir)\.nuget\NuGet.targets" 
            Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
    <!-- Line after the change 
         (relative path must match your .nuget folder location after the move) -->
    <Import Project="..\..\.nuget\NuGet.targets" 
            Condition="Exists('..\..\.nuget\NuGet.targets')" />
    

Once you do this NuGet package restore feature should already work for this solution. You can simply test it by deleting the Packages repository folder and rebuilding the project: it should succeed and restore the folder with all the required packages.

To add package restore support to other projects you only need to add the modified Import line from above to each of them, modifying the relative path as required so that it points to the actual location of the .nuget folder. Don't use the Enable NuGet Package Restore from Visual Studio any more as it will only add unnecessary files to the solution and incorrectly modify the project files.

Copyright
Creative Commons License