After experimenting with a json-based project format, Microsoft has returned to MSBuild as the foundation for C# and VB project files. Along with that decision came the promise to implement many of the much-liked project.json features. Today we’re going to talk about one of those features, NuGet integration.

Traditionally NuGet has been a bolted-on feature. While the compiler could trigger downloading NuGet packages, it didn’t understand them. So after a package was downloaded, it needed to be installed in the project. This may include updating assembly references, copying files, or running custom PowerShell scripts. This was rather brittle, and developers occasionally needed to manually cleanup the project files before reinstalling packages.

With the new PackageReference feature, many of those problems go away. Instead of referencing individual assemblies, developers can now reference the package itself.

Package references are also transitive. This means that if you reference a package, you’re done. You don’t need to explicitly reference every package that package requires. According to the press release, this can result in a 5x improvement in performance when installing or updating packages. In one example, a 10 minute process was reduced to 30 ms.

Solution-level package folders are also eliminated. Dependencies are instead referenced directly from the user’s package cache. If you are wondering why they didn’t do this before, consider again the “bolt-on” nature of prior versions of NuGet. Since the compiler didn’t understand NuGet packages, it required that “hint paths” be set correctly in the project file. Since each user could place their package cache in a different place, using it wasn’t an option. Thus the solution-level packages folder was created, ensuring the relative hint paths were the same for all developers.

Versioning

Versioning support for NuGet project references has improved greatly. You can now use ranges and wildcards for which version of a given package you wish to use. Ranges use a mathematical syntax common:

At least version x.y: [x.y

Greater than version x.y: (x.y

Version x.y or earlier: x.y]

Earlier than x.y: x.y)

For example, if you want at least version 1.4.2 but not 1.5, you can write “[1.4.2, 1.5)”. If instead you want any version in the 1.4 family, you can say “1.4.*”.

Content can now be controlled using IncludeAssets and ExcludeAssets tags. These are used to modify which types of assets (analyzers, content files, etc.) are included in the build process. You can even tag assets as private, meaning they are used for development purposes but shouldn’t flow to down-stream libraries.

Creating NuGet Packages with MSBuild

While you could always launch NuGet’s package command from MSBuild using an Exec Command operation and passing in a specification file, it didn’t work well in a continuous integration environment. So as part of this release, MSBuild can directly package projects. It will even work with projects that target multiple frameworks using the TargetFrameworks tag.

Speaking of which, you may find the need to reference different packages based on the target platform. With PackageReference, you can include a standard MSBuild conditional expression to indicate when the reference is applicable.

Backwards Compatibility Issues

A major concern about this feature is the lack of support for some older NuGet features such as content folders, XDT transforms, and the install.ps1/uninstall.ps1 PowerShell scripts.

Currently these features are available for .NET Core and .NET Standard projects. Other project types can use it if the VS 2017 Update 1 Preview is installed..