How to create "NuGet Package Management Project" for .NET Standard?

Viewed 625

I would like to work with a project whose only point is referencing NuGet packages, which (along with all of their dependencies) will then be downloaded and updated by Visual Studio (currently, 2017 for me).1 (This is somewhat similar to what is described in another question.)

When I create a .NET Framework 4.7.1 project, I can use VS's Manage NuGet Packages feature to pick the desired packages, VS will download the packages into the solution directory, and my build scripts can figure out which DLLs to copy into my actual project folders based upon the information found in the .csproj file (<HintPath> and such).

When I do the same with a .NET Standard 2.0 project, however, VS will use the new simplified .csproj format. All it mentions about the (explicitly and transitively) required packages is a single <PackageReference> element with just the package name and version for each package I have explicitly chosen to reference. Not enough for my scripts to transplant the references into the target .csproj file.2

Is there still a good way to manage NuGet packages via such a dummy project when targetting .NET Standard (2.0)?


1: Reasons for this include a large number of individual solutions, many of which need the same packages, and where not all developers should manage those packages themselves, and environments that do not support NuGet on their own (such as the Unity game engine, so a separate project to manage and pull NuGet references is the way to go).

2: Even if the configured "API Compatibility Level" in a Unity project is ".NET Standard 2.0", the .csproj file generated by Unity will use the "old-style" verbose format (and actually target .NET Framework 4.*, it seems) and accordingly expect an explicit path to referenced libraries.

1 Answers

As I mentioned as a comment to the question, ignoring the question footnotes this looks like a generic .NET question and I expect google will suggest this page to people making generic searches, not limited to Unity, and it won't be obvious to other people reading the question that it's specific to Unity until they read most of the question. Therefore, I'm going to start off giving an answer for generic .NET development.

If all the projects involved use PackageReference, then nothing special needs to be done. Simple create a .NET Standard Class Library from the new project template, add package references to the packages you want, then create project references to your class library from the apps that you want to use that set of NuGet packages.

If you have a non-SDK style project that does not have any packages, then you should add <PackageRestoreStyle>PackageReference</PackageRestoreStyle>. The reason is that PackageReference was added to NuGet years after NuGet started, with the original way to use packages being packages.config. So, when a project doesn't opt-in to PackageReference restore style, it defaults to packages.config. See the next paragraph.

If you have a non-SDK style project that uses packages.config (or no packages and hasn't opted into PackageReference restore style), then this approach may not work. The build system will build the app's assembly and probably copy all references directly referenced in the csproj, which means it will get your class library dll as well as any nuget packages directly referenced. However, since the app csproj doesn't know about your class library's NuGet packages, they won't be copied. Normally a class library references packages that it uses and the .NET build system will use something called ResolveAssemblyReferences to look at the class library dll, see what dependencies it has, then copies those dlls. However, in this case the class library project doesn't have any compiled in dependencies since it never used any of the NuGet packages the project referenced.

This is one of the reasons why the NuGet and .NET teams are encouraging customers to migrate to PackageReference. There's a bunch of issues, like transitive dependencies, and <HintPath>'s going bad when projects are moved around, that work much better as long as the project type you're using supports it.

Now, given that the question footnote mentioned that the asker wants to do this for a Unity project, I'll give an answer I hope works. Note that while I work on the NuGet client team, I know nothing about Unity and wasted about 2 hours of my morning today installing it and playing around trying to understand its capabilities just to find that Unity doesn't appear to support either NuGet or project references at all.

So my suggestion is to either:

use dotnet publish to have the .NET build system copy all the relevant dlls into a single folder, where you can then reference in the Unity project

or

Multi-target your class library to target both .NET Standard 2.0 and .NET Framework 4.7.1 (adjust the .NET Framework version to use the same as what your Unity project uses). Do this by changing the <TargetFramework>netstandard2.0</TargetFramework> to <TargetFrameworks>netstandard2.0;net471</TargetFrameworks> (note that the XML element had an s added to the end). Now when you build the class library the .NET SDK will create two directories in the bin\ directory, one for each target framework. At the moment at least, it seems that the net471 directory gets all the dlls, similar to non-SDK style projects.

Related