Update: Part 2
I remember, back in 2006 when I wrote my first managed add-in for AutoCAD. The fact that we could extend the functionality of a very big product, using .NET was huge. Till that time, if we wanted to use .NET for add-in functionality we had to rely on RCW or else we had to write messy and error-prone VBA code.
Today, anyone who builds applications in managed code (using .NET 4 and above) has built-in functionality for extensibility provided by the framework itself. In this post, we will be extending an ASP.NET MVC 3 application. We are going to use Unity as the Dependency Injection (DI) container and the types from the System.ComponentModel.Composition namespace (or else, MEF) for managing the composition of parts.
The host application, is the one shown below. I have selected the interesting types that I will be discussing.
A MEF-specific DefaultControllerFactory derived type. It gets the exported types with the contract name, derived from an IController type. After the controller is supplied, the MVC framework will resolve the Views.
A Unity-specific DefaultControllerFactory derived type. There are many implementations around. The difference from other implementations is that this one takes a delegate as a parameter in the constructor that acts as the fallback factory when the DI container can not supply a controller. This is a very important part of our architecture because here we have the chance to supply the target controller (as an add-in) using MEF.
Here we specify the default path for the extensions. We create a new instance of the DiscoverableControllerFactory class passing a CompositionContainer and a DirectoryCatalog. Keep in mind that the DirectoryCatalog is one of the many choices that MEF provides for discovering parts. Besides the creation of the DiscoverableControllerFactory we also create a new instance of the UnityControllerFactory class acting as the default controller factory. Any controllers that this factory can not supply will fallback to the DiscoverableControllerFactory using it's CreateController method. One last thing to note, this is the application's Composition Root. The DI container is referenced here, where the composition happens, and nowhere else in the entire application.
The add-in application is a regular class library and it's structure is shown below. I have selected the interesting types that I will be discussing.
This is a proof of concept Controller for this demo. It is decorated with the ExportAttribute and ExportMetadataAttribute. The later is needed in order to help the DiscoverableControllerFactory to choose the right controller among all the controllers supplied by this and other add-ins. The PartCreationPolicyAttribute is needed in order to specify that a new non-shared (transient) instance will be created for each request.
Nothing special to say here. The razor view is just any other (razor) view. The Web.config is needed as a hint for the MVC framework to compile the razor views at runtime.
Make sure to select all the views and set the property "Copy to Output directory" to "Copy if newer". This is important because each time we compile the add-in library besides the .dll with the models and the controllers we also want the views to be copied there (they are also part of the add-in).
You can download the demo application here. Upon build the Concepts.dll along with it's Views will be copied in the Web project's "Extensions" directory. When run, the application will automatically load the assembly the first time the "Concepts" tab is pressed.