Posted in:

My usual approach for creating NuGet packages is to have a Git repository per NuGet package. This is a relatively simple and straightforward way of working, but when you're creating with multiple NuGet packages that all depend on each other, it's not always ideal.

For example, with NAudio, I've often thought about splitting it up into several smaller packages, e.g. NAudio.Core, NAudio.Asio, NAudio.MediaFoundation etc. However, I'd want to keep all the source in the same Git repository, and use local project references between them while developing to make debugging easier. At publish time, though, they'd need to reference each other as NuGet packages.

I'd assumed for ages that this required some complex build script syntax to achieve. I'd even done loads of web searches to try to find out how other people were achieving this setup, but came up blank every time.

However, it turns out that this capability is built right in with the SDK style csproj files, and really easy to achieve! Just create a couple of class libraries, add project references between them, and when you run dotnet pack, it just does the right thing and creates NuGet packages that depend on each other properly.

How to set this up

To explain what I mean and how to achieve this setup, here's some very simple instructions. We're going to create two NuGet packages - LibraryA and LibraryB, with LibraryA depending on LibraryB. We can actually set up the entire project structure including creating a .sln file and setting up the project references, using various dotnet CLI commands.

Let's start by creating the two projects (class libraries), a .sln file that includes both projects, and then ask for LibraryA to take a dependency on LibraryB:

md MultiPackageLibrary
cd MultiPackageLibrary
dotnet new classlib -o LibraryA
dotnet new classlib -o LibraryB
dotnet new solution
dotnet sln add LibraryA
dotnet sln add LibraryB
dotnet add LibraryA reference LibraryB

If we then look at LibraryA.csproj, we see it's generated a project reference from LibraryA to LibraryB:

  <ItemGroup>
    <ProjectReference Include="..\LibraryB\LibraryB.csproj" />
  </ItemGroup>

Let's create an interface in LibraryB...

namespace LibraryB
{
    public class IMyInterface
    {
    }
}

...and a concrete implementation of it in LibraryA:

using LibraryB;
namespace LibraryA
{
    public class MyClass : IMyInterface
    {
    }
}

Now we're ready to actually create our NuGet packages, which we can do with dotnet pack (and I want a release build):

dotnet pack -c Release

This will create LibraryA.1.0.0.nupkg and LibraryB.1.0.0.nupkg. And just to be sure that the dependencies are set up correctly, we can use the excellent free NuGet Package Explorer utility to look inside the LibraryA NuGet package and ensure that it correctly depends on LibraryB:

NuGet Package Explorer

Of course, one challenge with this approach is that if you have an automated CI server that publishes the packages to a NuGet feed, how does it know which NuGet package has changed? Probably the simplest option is to always up-version all the NuGet packages in the repo and republish them. But that might mean you sometimes unnecessarily publish new versions of a package without any underlying changes.

Anyway, hope someone else finds this useful - I wish I'd known about this a long time ago. Big thanks to John Wostenberg for alerting me to this capability.

Comments

Comment by scottt732

“Probably the simplest option is to always up-version all the NuGet packages in the repo and republish them. But that might mean you sometimes unnecessarily publish new versions of a package without any underlying changes”
This bugged me at first (wasting space/repackaging unchanged code), but if you’re dealing with a modular library this is so much simpler to support. You don’t have to worry about package version ranges or users who’ve mixed and matched untested versions. I started to look at the version as a way to convey that all of this code was tested together.

scottt732
Comment by Roland Civet

I'm not an expert in the nitty gritty details of nuget, but I don't see much problem to up-version the main NAudio nuget. If the inner dependencies changes, I consider that the main nuget has changes, too. Your could add a new project in your solution such as NAudio.All that only references nuget dependencies in a single nuget file instead of local references (and keep local references outside of NAudio.All). The red flag is the path NodeJs npm took; it's widely complex as it provides way too much flexibility for dependencies (upgrade automatically minor versions in package.json). npm is one of the biggest regrets of Ryan Dahl (https://www.youtube.com/wat...

Roland Civet
Comment by Eran Bar

the main issue here is that libA nuget package will not include libB package reference and who ever consume libA nuget package will face problems.
how did you address those issues?

Eran Bar
Comment by Mark Heath

It does work - it creates two nuget packages and one references the other as I show in the final screenshot.

Mark Heath
Comment by Eran Bar

thanks, you are right, it works like a charm.
What if Project B has Project reference to Project C, do i need to include it under same solution or system knows how to build Project C dependencies?

Eran Bar