Over the years at my place of work, my rather skilful peers implemented various web-based electronic health systems. The systems were implemented using a variety of technologies, tools and languages that ensured that the products were delivered with functionality that met the requirements given a specified timeframe. Some tools were open-source whilst others were brilliantly written in-house like the recent content management system (CMS). Well done!
At the time of writing, ASP.NET development requires that all pages (for pre MVC), views, controllers and other content be in the web application project itself, which is a tad silly when you think about it. Some clever chaps (e.g. Portable Areas) have been working on ways to move the content to separate class libraries which while is an effective way to break up the project, is arguably sugar syntax as tight coupling still remains. To be fair though, their agenda was not to create a plug-in system as a primary requirement.
During the past month or so we have been discussing how we could implement a platform that would allow us to easily insert reusable modules of code, or widgets, into various areas of our web systems. Such a platform should ideally be web technology neutral, so as to allow clients to use ASP.NET xxx, Ruby, plain HTML and with the option of Java. Ideally it should be a plug-in-based system.
But isn’t that just a CMS?
…I hear you say? Perhaps. You could make widgets for a CMS that has arguably the same end result. But then again, if the system is just a shell and the plug-ins define the look, functionality and behaviour, then a plug-in system could do anything.
A CMS application could be a written using a plug-in-based framework, but not all applications based on plug-in frameworks would be CMSs. E.g. point-of-sale systems.
So how can we move content from the main web project to a stand-alone DLL that is not directly referenced by the application? First we need a plug-in system that acts as a mediator between the application and the plug-in. I wrote my own – the details I cannot disclose here. Anyway, the problem is not so much the plug-in framework, but how to redirect ASP to your code when looking for a view. The answer is Microsoft’s .NET class VirtualPathProvider. This class is quite kewl. It not only allows you to place MVC content in a different DLL but also anywhere else you want, even in a database! Apparently many CMSs use this approach.
You need to derive a new class from VirtualPathProvider and override the FileExists and GetFile method’s as documented. I whacked all this code into my special MVC service (no, not WCF) plug-in. My framework automatically registers namespace plug-ins (the ones with say Razor stuff) with my service so that when ASP comes along asking for a particular resource, I know which plug-in to grab it from. I’ve seen a lot of postings saying you must implement your own IViewEngine – that’s not entirely accurate. It’s only true if you wish to do things like make custom containers in which to place your widgets/views; make a CMS-style app; or smart-clients. If you just wish to have say a standard ASP.NET web app albeit with your own plug-ins, then the standard view engines will be just fine.
Now you will find that when creating a class library you will be unable to create say Razor views since it is not a MVC project/application. The easiest way to fix that is to create them in the web project then move them to the plugin project. The trick is to mark them as an embedded resource as described here. Initially I wanted to “pre-compile” them as described here but in my experience I could not get that to work (something about compiling in some ASP namespace), nor were they pre-compiled anyway. So I just embedded them.
Next I applied what I knew from my fun years writing Windows shell namespace extensions and Microsoft Management Console (MMC) snap-ins. I really liked these APIs compared to say IoC containers such as StructureMap, mainly due to the way the former uses strongly-typed contract-first patterns compared to the latter’s late-bound reflection behaviour. I find that when people use StructureMap they are using it like an elaborate class factory anyway with scary configuration. I find it is better to have zero configuration like the patterns in the Windows shell and MMC. My framework, which is a service container, automatically enrols and connects plug-in providers to plug-in services.
Not only does our new plug-in framework allow ASP.NET MVC 3 Razor views to reside in external plug-ins, but the main application should it so wish, is able to override the view and still gain access to the plug-ins model! Yay! This was an important “must have” that one of my peers expressed to me.
Also, because all the web applications are based on shell namespaces, IShellFolder, IShellView they are presentation agnostic, more so than ASP’s MVC. We could quickly churn out a native app using the majority of the existing code except for an extra interface for rendering to iOS, WinForms or GNU windows. The greatest thing is that now our entire website can be reduced to one or more plug-ins with the main app nothing more than a shell. Think Smart Clients.
I think the new framework is much easier than my first one – ESP. This current framework is my third generation plug-in system which is the culmination of feedback I have received over the years from my peers. There is no need for the GAC; the main app only requires a single assembly reference and one line of code to use the framework; and plug-in authors can make use of helper classes and wizards.
I dedicate it to you!