4

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.

O. R. Mapper
  • 20,083
  • 9
  • 69
  • 114
  • The `nuget package management project` you want is used to manage only .net standard projects or both .net standard,.net fx and .net core projects? – LoLance Jun 14 '19 at 06:47
  • @LanceLi-MSFT: Frankly, I do not quite know - in the Unity case at hand, I figure I need it to retrieve the .NET Standard 2.0-compatible libraries from packages, even though the target project technically appears to be a .NET Framework project. I prefer a solution that works the same way for all targets (to minimize reconfiguration when developing e.g. for Core in the future), but if that's not possible, the .NET Standard (and Unity?) compatible solution should be in focus in this question – O. R. Mapper Jun 14 '19 at 07:03
  • Can you please edit the question title and consider rewording your question body. If you ignore the two footnotes of your question the answer is simply to "create a .NET Standard class library". Everything else just works for pretty much any .NET project that isn't Unity. Also consider tagging unity since your question is not generic .NET and very specific to Unity – zivkan Jun 14 '19 at 16:59
  • @zivkan: Hi, I think there may be a misunderstanding here. As outlined in the first two paragraphs, the requirement is for the packages and their dependencies to get downloaded in such a way that all the required libraries can be found/copied automatically e.g. by a script. The third paragraph describes that this is not the case when using a .NET Standard project. While Unity may be a common example, such a process is required in any setting where I cannot rely on missing packages to get retrieved later on (e.g. during build). I will try and clarify the problem description, though. – O. R. Mapper Jun 14 '19 at 17:26
  • the .NET build system is supposed to take care of that. Only people creating their own project type, so in my personal opinion, Unity is lacking. Looking at their project system, it's so nearly a "normal" .NET project and therefore could reuse all of the normal .NET build system features to get the correct DLLs automatically, yet they chose to disable project and package references. – zivkan Jun 14 '19 at 17:29
  • If there are any other customer wanting to script getting all the dlls for a .NET Standard or .NET Core project, there's a good chance it's an XY problem and we can find a better solution finding out the real problem they're trying to solve, rather than explain how to find dlls by re-implementing what the .NET SDK does already. – zivkan Jun 14 '19 at 17:31
  • @zivkan: Hmm, indeed possible. To clarify, my thinking was that various situations I am currently somehow involved in have something in common: .NET projects that make use of NuGet packages, but where these packages cannot be downloaded on demand upon events such as building. Thus, somehow, download of the packages (and their dependencies) needs to be forced in such a way that all the required libraries are readily there with the source files. I recognize this situation in general in.Unity projects, but also in various parts of my workplace (e.g. most developer machines in our overseas ... – O. R. Mapper Jun 14 '19 at 19:22
  • ... department do not have internet access for (both internal and external) security reasons; or situations like when I get the current sources right before embarking on a journey where I can work for an hour or more, but cannot access the internet, either). Thus, I figured it cannot be that uncommon. If solutions for these situations are indeed very different, I may reconsider framing this as a Unity-specific question. – O. R. Mapper Jun 14 '19 at 19:24

1 Answers1

3

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.

zivkan
  • 12,793
  • 2
  • 34
  • 51