Using nuget packages is a pretty much standard practice in most companies, especially if there are many teams involved. At some point you need to create internal Nuget packages that other teams are going to use, but of course inside your solution you will reference the published project directly when you can.
The story goes
Once upon a time there was a solution structure that was happily buildable in Visual Studio. A solution structure, just like the one we see below:
Products.sln |- Common (1.0.0) |- Common.Services (1.0.1) |- Common.csproj |- Product.Common |- Common.csproj |- Product.Services |- Product.Common.csproj |- Common.Services.csproj
but then the Business Requirements enforced the Commons to move, and time of “separation of concerns”, or “sock” as it was popular between assemblies, arrived. After the “sock” nothing was the same:
Common.sln |- Common (1.0.0) |- Common.Services (1.0.1) |- Common.csproj Products.sln |- Product.Common |- Common 1.0.0 |- Product.Services |- Product.Common.csproj |- Common.Services (1.0.1)
There just wasn’t one solution anymore, new references had to be made.
Warning this story doesn’t have a happy ending
Kid, want some packages?
.NET Core CLI defines
pak as a command that will produce a
*.nupkg file, so let’s try creating
In Common project we are free of dependencies so we can
push the package wright away:
dotnet pak -c Release -p:Version=1.0.0 dotnet nuget push Common.1.0.0 -k your_nuget_api_token -s https://api.nuget.org/v3/index.json
That was easy, now let’s try publishing Common.Services
dotnet pak -c Release -p:Version=1.0.0 dotnet nuget push Common.Services.1.0.0 -k your_nuget_api_token -s https://api.nuget.org/v3/index.json
Also easy, but darn it, you forgot a feature to implement and it’s a small one, so you code it quickly, test it, up the version and
dotnet pak -c Relase -p:Version=1.0.1 dotnet nuget push Common.Services.1.0.1 -k your_nuget_api_token -s https://api.nuget.org/v3/index.json
In Porducts.Common we need to add the package reference to: Common package and then we can
dotnet pak -c Release -p:Version=1.0.0 dotnet nuget push Products.Common.1.0.0 -k your_nuget_api_token -s https://api.nuget.org/v3/index.json
For Products.Services we need to add the package reference to: Common.Services 1.0.1:
dotnet pak -c Release -p:Version=1.0.0 dotnet nuget push Products.Services.1.0.0 -k your_nuget_api_token -s https://api.nuget.org/v3/index.json
Unfortunately the second command is never going to happen, since you are going to witness
dotnet pak failing to retrieve package:
Common 1.0.1 which is completely normal since
that package doesn’t exist, but why does
dotnet pak is trying to retrieve it. Well as explained here specifying version in
-p:Version parameter, or as a
csproj version tag is going to override version of referenced projects, thus leaving you with a need for a non-existing package.
Explanation: Common.Services.nuspec contains a package dependency to: Common 1.0.1 which was generated when you created Common.Services package because the version of main project will override the references project versions, and since Common.Services has a project dependency to Common you will end up with a faulty nuspec.
Curious case of the missing package
The best workaround for this, or the one I liked the most, is usage of conditions inside of
csproj file. To be more precise we will setup 2 conditions: one for Debug and one for
Release mode. In
Debug mode we would like to use project reference, in
Release mode we will use the package reference to satisfy
<ItemGroup Condition="$(Configuration) == Debug"> <ProjectReference Include="..\Common\Common.Services.csproj" /> </ItemGroup> <ItemGroup Condition="$(Configuration) != Debug"> <PackageReference Include="Common.Services" Version="1.0.1" /> </ItemGroup>
Voila! Now you can debug and publish, but we still have to maintain the
Wouldn’t be nice if we could always get the latest version of
Common package? If that is something that works for you it can be achieved with small update using Nuget wildcards
<ItemGroup Condition="$(Configuration) == Debug"> <ProjectReference Include="..\Common\Common.csproj" /> </ItemGroup> <ItemGroup Condition="$(Configuration) != Debug"> <PackageReference Include="ProjectReferences.Common" Version="1.*" /> </ItemGroup>
Setting the version value to:
1.* will get the latest version of the package that has a major version equal to 1, if we publish
2.0 we will need to update the
csproj file also. The value of the version tag then should be:
Major versions must match!
This caveat exists because of another issue that is stopping us from using floating version like this:
[1*] which would enable us to
resolve packages that have major versions bigger then 1.
As you have been warned this story doesn’t have a happy ending, and the story still goes on, the assemblies still talk about the
sock although younger generation have started referring
to it as
If you want to reproduce this issue yourself your can use the this repo.