For all the marketing hype positioning DotNetNuke as a world class CMS (Content Management System), to me it’ll always be the ultimate .net Web Application Framework (WAF?). DNN began as a web app framework and in my eyes, it will always be just that. But then I’m old skool. I love the ability to change how something works. If you’re like me you love tinkering with what you’ve just bought. I’m almost never 100% satisfied with the way something works and the ability to be able to tweak it, excites me. If you’re like me you have a custom home screen on your mobile phone and tablet. If you’re like me you’ve taken a screwdriver to every PC you’ve owned. If you’re like me you’ve replaced so many hard disks you don’t care to remember and you’ve swapped that HDD for an SSD in your laptop because it was cheaper and way more fun to do it yourself than to buy it ready made. If you’re like me you love Open Source that you can understand and are allowed to adapt. So if you’re like me you love the framework aspect of DotNetNuke.
Whenever I’ve been asked for the “elevator pitch” for DNN I’d make sure to mention these things: (1) works on ubiquitous infrastructure, (2) separation of content, functionality and presentation, and finally (3) the installer. Why the installer? Well, without it we’d be writing elaborate manuals on how to get stuff to work. As a result we’d be swamped with support requests and we’d need to put a 4 figure price tag on our work to make a living out of this. Instead, the framework allows you to upload a zip file and you’re done. Wow. A simple zip file. And the site does something new (functionality) or looks different (presentation). This has sucked me in like a black hole sucks in light. No escaping.
I was fortunate enough to stumble on the framework when there were relatively few quality extensions and the marketplace was expanding rapidly. I launched the (in my opinion) first serious attempt at a document management module in 2004 and haven’t looked back since. It has been a fantastic ride. All this because DotNetNuke provided such excellent extension points and had such a simple way of delivering this to customers. I should also mention that at the time the business acumen of Brice Snow, the man behind Snowcovered (the forerunner of DNN Store) was paramount to developing this market as well. But my point is this: I wouldn’t be where I was today without the solid extensibility of DNN.
Much has been written about DNN module development, not least of all here in these blogs. And there’s an excellent Wiki writeup. I’m going to add my voice to this. Not because I think the rest is wrong. But because I think everyone has their own perspective and developed their own methods in this domain. Plus: in most module development posts I spot things that make me think “yes, that’s fine for a one-off module project, but it won’t fly/you’re overlooking something if you’re delivering to thousands of people”. And the more voices, the more readers have to be inspired by. So I hope you’ll find some of the things I write inspirational. Maybe there is something here that you’ll adopt and one year from now you’ll go “wow, that really helped”. Who knows. Instead of bullet points I’ve opted to just highlight the points I’m trying to make. Skim through the text by all means to see what I’m on about.
You might notice I’m not someone that jumps onto the latest fad. In fact, far from it. I need to see how a new technology evolves and relates to my world before I dive in. And even then I’d likely test it out in a small/open source module first, before using it in my main project. But the technologies I’ll describe are still very much alive and valid for DotNetNuke extension development. My technical goals are simple: create a module that will install easily and with the least amount of problems on as many DNN versions as possible while maintaining a lean codebase that is easy to maintain moving forward.
I haven’t enumerated all the instalments I’d like to write in this series. So no table of contents with links to future posts. Instead, you’ll just need to check this blog to see what came out. Maybe it’ll get referenced in a list somewhere else once, who knows.
Part 1. Project Setup
Setting up your development environment is discussed in many posts, so Google on this by all means. But in essence you need a locally running DNN and you’ll work in Visual Studio (or another IDE) inside that installation.
DNN
You’ll work inside DNN so you’ll have DNN installed somewhere on your dev machine. This is pretty simple to do and there are tons of resources to explain to you how to do this (e.g. here). Where I deviate from the default installation is that I use a database set up separately in SQL (rather than what is under App_Data). And I use an objectQualifier. In a later instalment I’ll get back on this and why this is useful. Choose something that is likely to remain pretty unique as a letter combination. So not “DNN_” but something like “NEWSDEV_”.
If you’re in this business, you’ll probably automate this at some point. I mean: installing DNN by hand? Really? So the technology I use is Powershell (check out Phil Beadle’s post for a good starting point). It allows me to (1) unzip the DNN zip file to the location it should go, (2) register the site with IIS, (3) create a database in SQL and finally (4) change the web.config to make sure the database is found. Then, to reach Jedi knight status, you can go so far as to load your own DNN templates and inject some modules you want to use in the Install folders. Get it? With the push of a button (well, it’s scripting, so it’s actually typing) you get your complete DNN environment set up the way you want. In my scripts I pass in Sitename, DNN Version and Template (which also loads any associated modules). And Bob’s your uncle.
If you’re still doing this by hand then creating a DNN site and wiring it up in IIS is trivial and described here for instance.
The Project
There are a few major decisions that you take right off the bat as you set up your project in Visual Studio. Are you going to go WAP or WSP? MVC or Web Forms? Or MVP? Or MVVM maybe? One thing that you have to deal with is that you’re working on a project that operates in a larger whole (i.e. DotNetNuke). That means you need to decide if you’re going to load the project together with the DotNetNuke framework all in one solution (i.e. both in source version), or if you’re going to just load your own project and link to DNN. I prefer the latter as an extension project typically is much smaller and running the complete DNN framework in debug mode makes the whole process of debugging a lot slower. If you’re familiar with the DotNetNuke codebase you’ll probably prefer this method, too. With “familiar” I mean: you know how DNN names and organizes stuff (namespaces/classes/methods/etc) so you can easily navigate classes to find what you’re looking for. I rarely, if ever, need to run DNN in debug mode for my projects (typically that only happens when I suspect there is a bug in the framework somewhere and I want to know for sure).
VS s*cks, sometimes
So how does that work, loading the project on its own? Well, this is where things get tricky. You see, Visual Studio is actually quite lame at supporting our kind of development. That may come as a surprise. But back in the early .net days some forward thinking people at MS came up with the idea of the Web Application Project (WAP) which allowed you to be modular inside a larger web app. This is where DNN has its roots (the good old IBuySpy days). Then it was pulled in VS 2003 (.net 2.0). No warning. Just gone. “Hey, how about us?” we clamoured (thanks Shaun who spoke on our behalf to the folks in Redmond). And MS came out with a patch and made some amends in VS 2005. But ever since that time I’ve had the impression of being a second class developer by the folks at Redmond. Anyway, before I take off in a rant: you need to be aware that this is a rough ride and there are some quirks you need to work around.
In essence, if you’re doing a Webforms WAP project like I do (and most of DNN’s module projects are like this) you’re creating a bunch of .ascx files under DesktopModules somewhere and a dll that goes to the bin folder. So in Visual Studio terms you’re somewhere between a web application (aspx/ascx project) and a library project. Neither quite cover it. Also you need to abandon hope that VS understands your asp.net HTML code. When you have an ascx open, VS is unable to find controls that come from DNN as it doesn’t know how to resolve the web site root. So it’ll happily underline all of DNN’s controls you use as unrecognized. But then again I gave up on VS’s HTML editor a long time ago when it was incapable of writing XHTML compliant code.
Templates
The best starting point, IMO, are Chris Hammond’s module templates or Steve Fabian’s Gooddogs templates. For WAP development you’ll want the former, but they are quite sensitive to having the right environment (have to jump through a lot of hoops). Steve’s templates load up immediately in VS and you can place them in your DNN installation of choice. But there are other templates as well: here for instance and more if you Google on “DotNetNuke Module Template”. What they do is to create a web project without a web.config. So not a web application as MS thinks it should be. This brings me to this tip: if you have a file called web.config in your project, delete it or your DNN will not come up properly with your module. It’ll break. ASP.NET doesn’t like have a web.config floating around in any other directory than the root directory. Note that when you switch the target .net framework of your project, VS will try to be helpful and create a web.config for you as it assumes you’re developing a monolithic site (sigh). Make sure to delete it when you see it.
Of course, once you get to Ninja status in module development, you’ll want to create your own templates. It’s actually surprisingly easy. A template is basically just a zip file with a (XML) manifest (sound familiar?) and a bunch of files that contain placeholders for things like project name etc. So you can create your own template by just grabbing an existing template and tweaking it with Notepadd++ if you were so inclined.
Module Foldername
So. You’ve just loaded an (empty) module project for your wonderful Widget module. And, as some module development guides will tell you, you will stick it in DesktopModules/Widget. Please don’t. Seriously. Just pause and think. If at any time in the future, some bright spark thinks of creating a “widget” module and that person’s like you, it’ll get stuck in the same folder. Now guess what happens when some other user comes along and installs both to compare which widget is better? Right, a big fat crash happens. You think this is not very likely? How many “Guestbook” modules have there been? How many “Gallery” modules? I don’t know exactly, but … a lot. And if we all go ahead and create our module directly under DesktopModules we get ourselves into a big mess in the future. Instead look at how Windows has solved it. Modules are to DNN what programs are to Windows. And programs go in the “Program Files” directory. So just as has become good practice in Windows programming, stick in your company name as subfolder first. So instead of DesktopModules/Widget, you now get DesktopModules/Acme/Widget as folder for your module. Provided your company is not really called Acme the chances of collisions are virtually zero going forward.
Note that the “old core modules” are all in violation of this rule. So creating a module that tries to install to DesktopModules/Events, for instance, is asking for trouble. I’m of the opinion that even DNN Corp should respect this rule, but until now they’re still creating modules directly under DesktopModules:
In fact, I also still see code creeping into the DNN Core framework which is incompatible with this naming convention. It doesn’t happen often, but every now and then it slips through (you need to keep in mind that most folks at DNN Corp don’t have a background in module development, so they don’t have the same perspective). I’m usually quite keen on catching these bugs and will report them immediately in Gemini. Please do the same if you spot something like that.
References/dependencies
Once you’ve saved your project in place, you’ll need to wire it up. That is: you need to tell it to compile the dll to the bin folder and you’ll need to hook the project up to DotNetNuke. The latter is done through “references” which create “dependencies” in your dll (e.g. “this dll was compiled against DotNetNuke.dll 06.02.00”). Now you could just load in the DotNetNuke dll that is in your development’s bin folder. And that would work just fine. But keep in mind that .net is very sensitive to dependencies. So if you compiled against DotNetNuke 06.02.00, then anyone who tries to run your work in a previous version of DNN will get a big ugly yellow error screen (or in case you keep custom error messages on it’s a little less ugly but equally confusing to end users). A dependency error. So if you’re developing for the masses, what is the best strategy? Well, I’ve stuck with the following routine. I’ll create a folder in my module folder called “_References”. In this folder I’ll copy the dlls I want to compile against. I’d choose the DNN version that has the minimum features I need and that I consider stable and current enough for my liking. This is somewhat of a dark art, but if you’ve been with the project for a while, you’d understand that 05.02.03 is, for instance, a good choice. But don’t hesitate to go DNN 6 if you need features in that version. You just need to make a gut estimate of the market you’ll want to sell to. My experience is that people are very slow to upgrade. And at conferences other module developers confirm this sentiment.
So from the _References folder I’ll now reference the dlls in my project. Make sure to change the “Copy Local” to “False” for the referenced DNN dlls as otherwise VS copies them to the bin folder every time you click build and your referenced Dotnetnuke.dll will overwrite the one in your dev DNN’s bin folder. Now the only requirement on your dev DNN installation is that it is more recent than the dlls you’re referencing. So I’d compile against 05.02.03, for instance, and develop inside a 06.02.00. This pretty much ensures you’re going to have a dll that will work across all these DNN versions. The reference to 05.02.03 means that any error with the API would be immediately visible in VS’s error list, and any error with 06.02.00 would show up on screen as you’re running your module. It’s not waterproof, but it’s close enough to waterproof for me.
Support Folders
If you’re like me (I use this phrase too often in this post, I know), then you’ll like to “keep things in one place”. So for my module development, I want to keep development titbits, build scripts, sql scripts etc, all in one folder. This ensures that source control just has one folder to watch and if I copy it somewhere else I know I have everything there. But this can get confusing if you also have code folders in your module folder (you wouldn’t be the first developer to distribute a module that writes a folder of junk to a users DNN just because you rolled it into the module’s install zip file by accident). An easy way to organize yourself is to use a leading underscore for all “support folders”. In my case “_References” as we saw before as well as “_BuildSupport” for build scripts, “_Install” for all installer related items (scripts, manifest), “_Packages” for all built packages, etc. It makes it very easy to keep an overview of where the code is and where your non-distributable stuff is.
A quick note here. In most module projects I see the SQL scripts (.SqlDataProvider files) appear under Providers/Data/SqlDataProvider. Yuck. I mean: that is one way to thoroughly hide/lose stuff. And as it is not compiled code, but rather “installation instructions” I feel they have no place there. Instead I advocate to keep scripts under “_Install/SQL” or some place similar and build them into the release package at build time. Cleaner IMHO.
Registering the module
Until now you’ve just made changes on disk. Now you’ll want to make sure it becomes visible in DNN. So you’ll need to head over to the Extensions page (Host menu) and from the module’s menu select “Create New Module”:
This will take you through registering the module in DNN so you can add it to a page somewhere. There are numerous sources on dnn.com about using this feature to register a new module so I’m not going to go much deeper into this here.
Debugging
With the project set up the way it is, you’ll need to tell VS what to do to begin debugging. In the project settings on the Web tab you can specify a url to hit when you push F5. Myself, I prefer to separate the debugging from the running. So the website will be up all the time as I’m working but I’ll hook VS up to the w3p process when I need it. This is done through “Attach to Process” or CTRL-ALT-p. Press “w” and you should find w3p quite easily:
Note you should have the one with “Managed” in it, as that is the worker process that is doing the managed code (i.e. asp.net).
Wrap Up
You should now have your set up so it allows you to easily add/change code and quickly see the effect in DNN. You haven’t written any code yet, but you’re all set to create that awesome module of yours. If you have questions, don’t hesitate to tap me on the shoulder at this year’s DNN World and ask me.