Written By Peter Donker
2019-07-23
I have said on many an occasion. And I'll readily admit that in part this is just an old coder's frustration with a new technology. One of the many "get off my lawn" moments I've gone through. But like it or not, NuGet is here to stay and in our Microsoft-oriented world it is the standard way to resolve references. Before we had NuGet we'd do this "manually". So before I go any deeper into this, let's first explore what the breadth of the problem is and how we used to do this in the old days. You know, when telephones were still attached to a wall.
Background
Way back when, DNN was a sample project for asp.net called IBuySpy. If you wanted to develop a module to extend it, you'd load in the entire IBuySpy solution and add your code. This would ensure that (a) all your references were correct and (b) you could debug by drilling through the entire solution. This way of development is still done in some systems but there are huge drawbacks to this. One such reason is that the more code you have "open" the slower VS and compiling becomes. For the current DNN platform you'd load in so much code that your development speed would drop to naught. So no one does this (or not anyone I know in any case).
In the early days of DotNetNuke we used so-called WAP ("Web Application Project") projects. The idea was you'd develop your module in its own project and then reference the relevant dlls from the bin folder of the larger solution. This is how you'd do DNN modules and this is where many of us jumped on board back in the early 2000's. And this has worked quite well for us ever since.
As time went on I noticed one important difference between how we'd do module references and how all tutorials on the web explained how it should be done. If you develop to distribute you'd typically want to compile against an older version of DNN while ensuring yourself it will run on the latest as well. This is easily done by keeping a folder of referenced dlls with your project and not rely on the dlls that are in the bin folder of the DNN installation you're using to develop on. Ensure that "Copy Local" is set to false for the various DNN dlls and you're good to go. It requires a few tweaks and understanding of how VS deals with references, but it works well for the development of modules that need to work on a wide range of DNN versions.
NuGet
So along comes NuGet in 2010. Now, if we wish to use Newtonsoft for instance, we'd use NuGet in our project to create the reference. The advantage that we're promised is that we're no longer keeping a directory of reference dlls and we don't need to fish those out of DNN any more. The references are neatly listed in an XML file and VS takes care that the projects use the right dll. But, as is so often the case, those who create these new technologies are not in our shoes and have designed them for very different scenarios.
Where it gets complicated
DNN currently gets distributed with around 45 dlls. Generally you do not want to touch these dlls. They interdepend. And as you're not aware of how exactly, changing any one of them risks breaking the entire application. So as a rule of thumb: if you have any dependency on a dll that ships with DNN you do _not_ include this in your module's install zip. Period. No ifs or buts. I know DNN can work out if an assembly has a newer version or it can be overwritten etc. etc. But just, no. If you're using Newtonsoft or SharpZipLib you don't want these dlls to be distributed with your module and instead your module relies on the ones that came with DNN.
What this entails is that you should be aware which dlls were shipped with the distribution you're compiling against! If you're compiling against DNN 7 then you must use Newtonsoft 7.0.1 (or earlier) as a reference. This will ensure your module won't be broken on DNN 7 and since DNN took care of rewiring newtonsoft when it was upgraded to 10.0.3 in DNN version 9.2.0, your module will work unaltered on that version, too.
What this all means is that if you wish to use NuGet, you need to first look up which version of Newtonsoft was used for DNN 7. And this goes for every dependency that you take that is already included in the distribution. A recent MVC style module I have worked on includes references to 11 dlls that are not built by DNN but are part of the distribution (think Microsoft.ApplicationBlocks.Data, System.Web.Http, System.Web.Mvc). I will now need to check the version for each of these for the DNN version I wish to compile for. Can you begin to see my frustration with this "improvement"? And to top it all off, many of these dlls are hiding in other packages from Microsoft with a completely different name and version number (System.Net.Http.Formatting.dll is found in the Microsoft.AspNet.WebApi.Client package for instance).
The twist
But, I hear you say, you can bundle things in NuGet so you could create a package that combines things. This indeed would solve the issue above. But from personal chats with "NuGet experts" I hear the Microsoft philosophy is away from bundles. I can only guess why, but again I'm left with the impression that the good folks behind this have no idea how coding looks like to me.
I have tried to make a case internally for a "everything but the kitchen sink" DNN NuGet package. I.e. one that includes all dlls and dependencies we need. In theory this would be the best solution as anything you develop for DNN would be covered by such a package. But there are a couple of snags. For one VS insists it will add all assemblies in the package as references for a project if you add the bundle. So using such a NuGet package would explode the number of dll references in your project. It's not the end of the world, but it makes things a bit unwieldy. The second disadvantage is that for some dlls we have no right to distribute them this way. So there are legal barriers to this, too.
And that's not all
Just like other package management systems, NuGet can retrieve packages from multiple sources, or "feeds". By default it takes its feed from nuget.org. But you can add feeds to your own NuGet server, a share, or any other provider. This means that your project can reference packages that someone else, who downloads your solution, cannot access unless he/she changes their configuration. This is what happened to me when around 2018 when I tried to build the DNN source code. There were bunches of errors in NuGet stating it could not find packages. And unless you are familiar with how NuGet works, you are left wondering if and how you'll get this working. After outside help, I got the url to the DNN Corp NuGet feed and those errors disappeared. But again I was left feeling this is a pretty awful experience as a developer.
This has recently improved with local nuget config files that allow you to specify the feed locations. This will resolve this issue.
Where we stand today
I'm still on the fence on how useful NuGet is for DNN developers. The current set of packages is still incomplete and doesn't go back in time. The DotNetNuke.Core and DotNetNuke.Web.Mvc packages, for instance, are only there from DNN 9.2.0 and newer. And as long as it doesn't cover our needs I am not surprised to find that when I ask around in my developer circle of friends, most will say they don't use NuGet for this reason. They are still using the trusted folder of referenced dlls.
Do you have a solution?
I think we should have clearly delineated bundles for the most common use cases of DNN module development (Mvc, Spa). And that these bundles should go back as far as we can reasonably create them (e.g. DNN 8.0). This does not need to be a lot of work. In fact, I have already automated this process. All it needs is the "templates" for the packages to be created. These packages should be clearly labeled so everyone knows these are the official/best DNN development packages. Since we are stuck not being able to retract or improve the old packages I propose a new banner and/or logo.