Debugging a NuGet Package

NuGet adds and manages DLL files in your .NET project, but not any source or symbol files. This can make debugging packages without extra tools like ReSharper rather challenging, since you’re basically left analyzing the source code without stepping through it at runtime. This is a short guide to debugging a NuGet package by replacing it with a local build.

Some Background

I’m eternally grateful for open-source solutions to hard problems. I’m also grateful for package managers (which are themselves solutions to a hard problem), but neither are perfect all the time. One of NuGet’s limitations revealed itself to me on a recent project that utilizes CSCore, a library that applies the magic of ✨abstraction✨ to COM objects, turning them into much more manageable components.

While hunting down a bug involving both concurrency and COM, our execution moved from our own code into that of CSCore. Specifically, it entered a procedure containing the ominous comment: “//possible deadlock”. Unfortunately, it was impossible to track the CSCore object’s state just by looking through the source code. Furthermore, the important properties were private so we couldn’t inspect them while debugging. We also didn’t have ReSharper available to use Assembly Explorer, nor any other decompiler. And because CSCore doesn’t provide a PDB file, that left us with no way to inspect the behavior at runtime. With all of the canonical debugging tools unavailable, our eventual solution was to take the reins from NuGet.

Be Your Own NuGet

Okay, obviously you do not want to stop using NuGet altogether! But you can manage a single package on your own, just for debugging purposes:

1. Make a new branch for debugging the package.

You won’t want to keep any changes, wince you should let NuGet do its job whenever possible.

2. Clone or download the package’s source code

Make sure it’s the same version NuGet installed! You can put the source code anywhere, but putting it in or adjacent to your project makes the most sense in my opinion.

3. Open the library’s solution in Visual Studio and run a build

This is probably where you’re most likely to encounter one-off issues; the library may require a different .NET version than your main project, or some other dependency that isn’t included in the source code. If you have to debug your debugging strategy, then proceed carefully and remember to periodically re-evaluate the tradeoff between getting the library to build and moving onto a new strategy altogether. One additional note: while testing, I ran builds with the Debug configuration, but a Release build might behave differently.

4. You should now have a fresh, locally-built DLL

But more importantly, you should also have a PDB file (aka symbol file). The generated DLL actually references this PDB file so that applications like Visual Studio can connect assembly from the DLL to the source code while debugging.

5. Now that you have your own DLL,

go back to your project in Visual Studio and uninstall the dependency through NuGet’s CLI or GUI. This should remove the package from the References listing inside the Project Explorer.

6. Re-add the DLL reference

Right click on References, then Add Reference… > Browse…, and navigate to/select the DLL you created. It’s probably inside of the bin inside of the dependency’s project directory. At this point, your own project should behave exactly as it did before removing the NuGet package. If it doesn’t behave as expected, you might not have built the version of your dependency that NuGet installed.

7. Go forth and debug!

Any time you rebuild the dependency, a new DLL file will be created. Since your main project references this DLL’s location, you don’t have to rebuild your own code in between test runs. Add Console.WriteLines to your heart’s content, and take full advantage of breakpoints and step-through debugging. The dependency’s source won’t be available in Solution Explorer, but you can open individual files with File > Open > File… and then set breakpoints as usual.

That’s it! If you discover a defect in the package, then just fork it, fix it, and submit a pull request. Hopefully the library was working properly all along, and you’ll only need to edit your own project’s code.