Recently I was attempting to create a shareable library of code in C# to interact with a new enterprise document storage solution. The document storage solution essentially stored metadata about a document in an onprem database, but stored the document itself in Azure blob storage and kept a reference to the blob in our database. To interact with this service I created an pretty basic web api with just a few endpoints that did some CRUD operations. The api was secured using a client id / client secret provided by our Okta instance.
My main goal with this library, as with all shareable code I write, was to make it as simple as possible to implement the code and interact with the new document storage solution. The code behind this shareable library offered a quick configuration method that setup some services for dependency injection and a factory class that would retrieve those services for the developer, all the while handling authentication in the background so they didn't need to think about it too much. This code had the potential to be used by any group in our organization so simplicity was key as well as access.
I decided to create a nuget package of the code that we could host in our Azure artifacts nuget feed. If you don't know what a nuget package is, it is essentially a zip file with the .nupkg extension that contains compiled DLL files and any supporting files needed for the shared code.
I've created nuget packages before, but not in quite a while and it seemed like some things had changed since last time. After multiple google searches I was able to piece together something that worked very well to get a nuget package with .net 6, and thought I'd share here...
A couple requirements I had for the package:
- It needs to be part build pipelines in Azure and get an automated build version.
- It needs a README file that popups up in Visual Studio with documentation on how to use the code.
So first I created a nuspec file which is essentially a file that defines what needs to be in the package and some metadata about the package itself. It looks like this...
The parts I want you to take notice of are
<id>NHA.Document.Client</id> <version>$version$</version> ... <readme>README.md</readme> <dependencies>...</dependencies> <files>...</files>
The id field must be some unique id. If you were to publish this on https://www.nuget.org/ a unique ID is necessary.
The version is dynamic because of the $version$ parameter we will pass to it. More on that in a bit.
Dependencies are any other nuget packages that this code relies on. In this case I use Swashbuckle for some data annotations.
The files section is probably the most important. Here you must list out the files that are necessary for the actual usage of the client library. Any .dll, .pdb files that are compiled for this library must be listed here. Also, if you want to have a readme file popup upon installation you must list it here. The src value is just a file path to where the file exist. Since it is compiled code it will be placed in the bin subdirectory aligning with the .Net version you're on.
A note on README documentation.
I'm still a little confused on it, but I believe the case is that if you have a readme.txt file it will automatically popup upon installation. With a .txt file you are sort of limited in how nice you can make it look. If you want a nicely formatted markdown readme that displays on the nuget.org site when you publish it then you also need that file in the package and have it referenced in the tag of the nuspec file.
Once I created the nuspec file and saved it at the root of the client library as NHA.Document.Client.nuspec I was ready for the next step.
The next piece was to modify the .csproj file for the client library I was packaging. It looks like this...
The main part to take notice of is
<PropertyGroup> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <NuspecFile>NHA.Document.Client.nuspec</NuspecFile> <NuspecProperties>version=$(PackageVersion)</NuspecProperties> </PropertyGroup>
This section says where to get the .nuspec file definition for the packaage and sets up our $version$ variable. In our case we are going to get the version from the actual build id and pass it in when we use the dotnet pack command. More on that in just a sec...
There is another section below... but if I'm honest I don't for sure if it's necessary :) ... I don't remember adding this and I am unsure how it got there... either way here it is.
<ItemGroup> <None Update="README.md"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <Pack>True</Pack> <PackagePath>\</PackagePath> </None> </ItemGroup>
With that we are ready to pack it up with the "dotnet pack" command. We need to point the pack command at the csproj and pass some additional arguments.
dotnet pack NHA.Document.Client.csproj -p:PackageVersion=2.0.5 -c Release
If I run this command at the root of the client project it will find the csproj file which then points to the nuspec file. Using the -p: command we can pass in variable values. In this case we pass in a version number. The -c command is for the build configuration to use, in this case I use Release. You can run this from the command line if you have dotnet installed or include it in your build yml files. In my case the command in my yml files looks like this...
--target=pack --build-arg VERSION="$(Build.BuildNumber)"
Using this command gets the build number from the build and that becomes our package version. Our build pipeline kicks off automatically every time we PR to the main branch.
So there it is... how I build nuget packages from a dotnet 6 class library. Probably not terribly exciting to many, but I know when I was working on it I did a lot of google searching and trial and error some hopefully this helps anyone else trying to do the same thing.
Top comments (5)
I have a related question for you. Let's say I've got a solution with about 5 different class libraries, some of which depend on the others. All of these class libraries I want to publish as NuGet packages.
I tried #1 earlier this year and it was really awkward for a development experience.
Oh, interesting. I'd say: If the projects can live independently, I'd go for #1 as people can then bring only what they need. If, however, a particular DLL has no use on its own, I'd bundle it in the same nuget.
All this comming from a no-experience-in-the-matter guy. I have only ever published one nuget and did not have either scenario. Just an uninformed opinion. 😃
That's about my guess as well. I think option 1 might need a modest bit of automation to be as pain free as possible. Or run a private NuGet feed that's faster.
Option #1 does seem a bit nightmarish as far as working on the code and making sure that all the individual projects have the most up to date version of the other nuget packages. If the other projects can't live on their own I would use project references and bundle them together. That is essentially what I had in my example above. I've experienced too much of what I call "nuget hell" to lean on the packages too much.