One of the great promises of .Net was the escape of DLL Hell. While the .Net framework has reduced it from uncontrollable levels, any experienced developer can tell you it’s far from gone. Especially when you need to write code and then deploy it to multiple servers. Fortunately, there are tools that can help.
Our new best friend is NuGet. Let’s play out an example.
Let’s say we have a logging and utility assembly we’ve written. It will handle logging of anything we want. It also includes a number of utility functions we need. We’ve built it and it works great!
We are working on our “real” project and have included out logging and utility assembly as a reference.
Now, in our code, we can call it very simply.
Easy, right? Well, naturally, since our Logging and Utility assembly is a shared assembly over many projects, and since it won’t change much, we shouldn’t include the source code in our project, we should just reference the compiled assembly. Here’s the solution with the referenced assembly. Notice the Logging and Utility project is not part of the project.
This is easy! What’s the big deal? Let’s get this code deployed!
The standard way to deploy code (at least using TFS) is it initiate a build script. The build script would pull in the source code and execute the MSBUILD.exe command to compile things. However it will need to pull in the LoggingAndUtility assembly. If the LoggindAndUtility assembly is on a shared network drive, things should be in good shape. The build script will pull in the the assembly, compile the source code and get it moved out to the deployment server.
So what’s really the problem?
As is always the case, we run into versioning problems. When the LoggingAndUtility assembly actually does change it can open Pandora’s box and let all the troubles in the world out.
Developer A updates the LoggingAndUtility assembly and now it’s at version 1.0.1. Developer A deploys it out to the shared network drive and updates the build script. Developer B is working on the real project but doesn’t have 1.0.1 so her code is still configured to expect version 1.0.0. She checks it in and deploys. BOOM. Failed build due to referenced assembly version mismatch.
Naturally, if Developer B updates all her code and references to make her project expect v1.0.1, things will be back in order. This certainly works, but it’s a hassle to figure out where the problem is and how to resolve it properly.
NuGet to the rescue! Anyone that has included Microsoft Patterns & Practices, NewtonSoft.JSON or any number of other projects in their own solutions has used NuGet. According to NuGet.org:
NuGet is the package manager for the Microsoft development platform including .NET. The NuGet client tools provide the ability to produce and consume packages. The NuGet Gallery is the central package repository used by all package authors and consumers.
This is great! However, NuGet is more than just the publicly exposed NuGet Gallery. You can host your own NuGet feed. For us, this means that we would compile our LoggingAndUtility assembly to a NuGet package and then include it from our local NuGet location. Further, our build server can be coded to do a NuGet Update Packages to bring in anything that’s needed including any updates to the packages so that it’s always got the proper version.
The Mechanics of How
First of all, “The Mechanics of How” sounds like an awesome title for a science-fiction story.
Okay, now that we have that out of the way, here’s how you build, deploy, host and consume NuGet packages.
First of all, you will need to get your hands on NuGet.exe. If it’s not already on your computer, click https://nuget.org/nuget.exe to download it.
Set and Get the assembly and file versions
In Visual Studio, you need to get and set the file and assembly versions for your project. Right click on the project and select properties.
First, make note of the AssemblyName. This is probably well known already, but we’ll need that information later on. For now, click on the “Assembly Information” button.
This will open a window that will allow you to set the file and version window. I find I have the best success when I set both values to the same value.
Build and compile your assembly. Now it’s time to move on.
Create the Specification file
There are many ways to create the artifacts. We are going to create our NuGet package from the project directory. For more information visit https://docs.nuget.org/Create/Creating-and-Publishing-a-Package
Open a command prompt and navigate to the directory where your project file is. For this example, we have our LoggingAndUtility project in the standar Projects directory within the Visual Studio 2013 folder of MyDocuments. The file path to that is:
C:\Users\<user name>\Documents\Visual Studio 2013\Projects>
For our LoggingAndUtility project, the .csproj file is nested within there.
Now, we can execute the following command. I have to specify the path to the NuGet.exe executable, but you might not have to.
This will create a default specification file.
Edit the Specification file
Using a text editor, open the .nuspec file we just created. There is a GUI editor, but I have found that to be problematic. Editing the file with Notepad works just fine.
- $id$ – This is the AssemblyName we gathered from the properties window
- $version$ – This is the file/assembly version we got and set previously
- $title$ – This is the user friendly title of the NuGet package
- $author$ – This is the author of the package (you!)
- $owners$ – This is who owns the package (also you!)
- $description$ – A description of the package
In addition to those variables, there are other things that should be set or removed.
- licenseUrl – If you want to have the consumer agree to a license to use your assembly, you can refer to the license here. This node can be removed
- projectUrl – If you want to provide a “homepage” for your project that is available in the NuGet description, it can be listed here. This node can be removed
- iconUrl – If you want to provide a custom icon to set your listing off in the NuGet search results, put the url to the icon here. This node can be removed
- releaseNotes – As you version your assembly, you can list your release notes here.
- tags – You can help users find your assembly by tagging it
Here’s what our LoggingAndUtility.nuspec file looks like after updating it.
Compile the package
Now we will issue another NuGet.exe command to compile the package into a deployable unit. The command is
nuget pack LoggingAndUtility.csproj
Now, we have our .nupkg
Deploy & Host
Under normal circumstances, you would upload your package to NuGet.com or some other webserver. For us though, we are simply going to share our packages on a network share. This is the easiest way for small teams to share things. Simply copy your .nupkg file to a known location on your network.
Here, you can see that I have several versions of the LoggingAndUtility package out there.
We’re in the home stretch now! Consuming NuGet Packages is a simple and well known operation. Adding our custom package source might not be as well known.
In Visual Studio, select Tools > Options. Then select “NuGet Package Manager” from the navigation tree (it might just say “Package Manager”). Open the tree node. Make sure both checkboxes are checked.
Next, click on “Package Sources” in the navigation. Click the “+” button at the top of the panel. This will allow you to add a new package source. Give your package source a recognizable name and then set the source parameter to your network location where you deployed your .nupkg file.
Now, we can remove the LoggingAndUtility project from our solution and add it back in using NuGet. When you remove the project, you will also need to remove the reference. We’ll add the reference back in from NuGet.
Make sure you select the appropriate package source from the list of Online package sources. Notice how the values we used in the .nuspec file show up in the NuGet listing.
Click the Install button. Nuspec will install the package for you. You’ll see the familiar green checkmark to indicate it’s been installed. If you check your references, you’ll see that it’s there for us.
Let’s bring this back around… what did going through all this get us? A few things… first of all, we can include dependencies. For example, if our LoggingAndUtility assembly relied on Newtonsoft.Json, we could mark that in our .nuspec file. Then when we added our LoggingAndUtility assembly, Json would automatically come along for the ride.
The other major benefit is that updates to included packages become supremely easy and can be scripted during the automated build and deploy. To update an installed package from within Visual Studio:
Select Tools > NuGet Package Manager > Package Manager Console
In the Package Manager Console, issue the command to update-packages
Notice how it automatically removes the old version and updates with the new version.