Lately, we had a task in our team to create a NuGet package that would encapsulate logging, providing all the bits and pieces out of the box. We wanted it to be as easy to use as possibile, so the idea was to add default configuration to your appsettings.json on install. That’s something which used to be relatively simple with framework, where you could just transform Web.config to include library’s config sections. However, I got seriously stuck when trying to accomplish the same in dotnet core

I quickly realized I’m not quite up to date on how things work now with core and that I need to catch up a bit. As a result of this investigation, I wrote this summary to help my future self and others clear up the confusions. So, if you ever get stuck on creating NuGets, I hope you’ll find this article useful

How things work in .NET Framwork

So, in the “old days” of .NET Framework the very first thing you needed to do is to grab nuget.exe binary. For some reason, it’s not included in any version of Visual Studio. You can find it in the Downloads section of nuget.org.

Then, you need a nuspec file which is the manifest of your newly created package. It’s meant to describe the metadata, dependencies and content of a package. It could be bootstrapped with nuget spec command, which will create a scaffold of this file for you.

Example nuspec could look like this:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
    <metadata>
        <id>AwesomePackage</id>
        <version>1.0.0</version>
        <title>AwesomePackage</title>
        <authors>AwesomePackage</authors>
        <owners>urbanonsoftware.com</owners>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <licenseUrl>https://opensource.org/licenses/MIT</licenseUrl>
        <projectUrl>http://contoso.com/AwesomePackage</projectUrl>
        <iconUrl>http://contoso.com/icon.png</iconUrl>
        <description>My awesome package</description>
        <releaseNotes>More bugs incorporated</releaseNotes>
        <copyright>Copyright 2019</copyright>
        <dependencies>
            <group targetFramework=".NETFramework4.6.1">
                <dependency id="Newtonsoft.Json" version="12.0.3-beta2"/>
            </group>
        </dependencies>
    </metadata>
    <files>
        <file src="bin\Release\AwesomePackage.dll" target="lib\net461" />
    </files>
</package>

Having this file along with the sources, nuget pack command will create the package out of it. It’s probably worth noting that nupkg files are nothing more than plain old ZIP archives. Change the extension, unpack it, and you’ll be able to see what’s inside.

We can also push the package to a feed using nuget push command. Probably, you might also like to specify the URL and credentials when doing so:

nuget.exe push -Source {NuGet package source URL} -ApiKey key {your_package}.nupkg

I won’t cover there the details on how to get the ApiKey as it might vary depending on the feed you are using (Azure DevOps, Teamcity, self hosted, etc..).

That’s really it when it comes to creating packages with framework. Basically, you call for a nuspec and there is no other way to create packages. Unfortunately, the process of creating nuspec file by hand can be very tedious and error prone.

Although there were GUI tools which eased the pain somehow, like open source NugetPackageExplorer, it was still hard, especially when it comes to dealing with dependencies. You had to specify all of them manually.. ouch..

Dotnet core to the rescue

Dotnet core eased the pain somehow, making the process of creating nuget packages a lot easier. First, Nuget is now part of dotnet SDK, and is available through dotnet nuget command. This is especially cool when you consider CI environments - no need to install additional executables anymore! Btw, you shouldn’t use nuget cli with dotnet core.

Second, dotnet cli supports generation of nuspec on the fly, based on the csproj. If you bring up properties window of a project targeting core, you’ll see a new Package section.

Visual studio package section

If you fill in this section, it will save these settings to the csproj and from that file we can then easily create nuget just by firing dotnet pack. It’ll translate all the dependencies and metadata set on csproj to a nuspec file and create the package. Actually, this way you don’t even see nuspec anymore.

Aand, we can make it even simpler by checking “Generate NuGet package on build” or adding following snippet to the csproj:

<GeneratePackageOnBuild>true</GeneratePackageOnBuild>

Then, nupkg will appear alongside binaries (bin\Release) after each build. Neat, isn’t it?

Include json configuration in nuget package

Unfortunately, there are still some cases with dotnet core where you might want to fallback to barebone nuspec. For example, embedding content files into the package is not supported with csproj. By content files, I mean things like json files containing configuration snippets, e.g. logger settings.

Lucky for us, there is an option available in csproj allowing us to use custom nuspec files. That is the NuSpec node, which needs to be placed under PropertyGroup:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
  	[...]
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <NuspecFile>PlaygroundNugetPackage.nuspec</NuspecFile>
    [...]
  </PropertyGroup>
  [...]

Then, we can define a nuspec which will copy json files to contentFiles directory. This is kind of special directory. If you place files within it, they’ll get copied to the root of the project using them. So, to get it working, you need to:

  1. Create contentFiles/any/any directory structure within the package solution.
    The any there means: any frameowrk, any cpu. You might constrain the files to particular framework/cpu architecture there if you wish
  2. Create contentFiles section in the nuspec which will include the files.
    All the paths there are relative to contentFiles directory
  3. Copy files to the budle using file node with target set to contentFiles
<?xml version="1.0"?>
<package>
  <metadata>
    <id>AwesomePackage</id>
    <version>1.0.0</version>
    <title>AwesomePackage</title>
    <authors>AwesomePackage</authors>
    <owners>urbanonsoftware.com</owners>
    <description>using nuspec with dotnet core</description>
    <contentFiles>
      <files include="**/*.json" buildAction="EmbeddedResource" copyToOutput="true" flatten="true" />
    </contentFiles>
    <dependencies>
        <group targetFramework=".NETFramework4.6.1">
            <dependency id="Newtonsoft.Json" version="12.0.3-beta2"/>
        </group>
    </dependencies>
  </metadata>
  <files>
    <file src="bin\Release\netstandard2.0\*.dll" target="lib\netstandard2.0" />
    <file src="contentFiles\**" target="contentFiles" />
  </files>
</package>

Have in mind though, that this way you need to maintain nuspec all by yourself, just like with .NET Framework. For example, if you add dependency to your package, like Newtonsoft.Json, you need to remember to include it in the nuspec.

By using NuspecFile in your csproj, all the things configured under Package tab will get ignored. Again, you need to maintain the whole manifest manually. Though, dotnet pack command and “Generate NuGet package on build” will work as before.

You might ask about Web.Config/App.Config transformations from .NET Framework times. In case you didn’t have the questionable pleasure of using them, they allow you to append/transform some xml nodes to existing project configuration.

It turned out, this feature is not supported at the time of writing with appsettings.json. As per the GitHub issue there are no plans to implement it either.

Trying it out locally

Before we finish, I wanted to show you one more neat trick. You can easily test your packages locally, without going through the CI pipeline or pushing them to the feed. The trick is to add the directory containing package(s) to nuget sources in Visual Studio.

Go to Tools->Options -> NuGet Package Manager -> Package Sources and add new source which will point to a directory lying somewhere on your disk. The packages will appear in the Manage NuGet packages window from now on.

Summary

Dotnet core simplified sharing code through packages a lot. Fill in the “package” section in VS, and you’ll get nupkg generated on the build with very little effort.

However, this is not a silver bullet and has some notable limitations when compared with manually crafting a nuspec manifest. Lucky for us, we can still use the “down to the metal” approach when needed. I hope this guide helped you to understand what’s possible and what’s not, so you can make an informative decision in your team.