Plugins in .Net with Castle Windsor

Plugins can be a useful way to extend the functionality of your application without having to re-deploy it, re-build it or, possibly, even re-start it. Plugging new functionality into your application can be tricky however it can be greatly simplified by using Castle Windsor.

DI-Based Plugins

In a well-designed application the IOC container should be the backbone of the application, facilitating construction of and access to the parts of the application which allow for interaction with the domain. This common point of contact running through the application provides a convenient way of plugging functionality into the app if we can modify the container.

Injecting Into The Injector

Castle Windsor provides a really easy way to accomplish this task by providing us with the following convenient method chain:

container.Install(  
    FromAssembly.InDirectory(
        new AssemblyFilter("<PluginDirectory>")
        )
    )

This can be called wherever you have a reference to the Windsor container (in the Global.asax.cs class in a .Net MVC or Web API project for example) and Windsor will take care of loading the assemblies for you and installing their dependencies as long as they contain at least one class which implements IWindsorInstaller.

This will allow you to install your new dependencies in the usual way and since the existing components of your application will already be loaded and registered you can reference them as long as you still comply with the application design.

How Big a Slice?

Plugging into the IOC container allows us a great degree of flexibility in how much of the application we can choose to plug in. Since we can reference existing pieces of functionality we do not need to necessarily build all of the required layers of our app for each plugin, we can simply add the layers we require such as the API and Domain entities without worrying about implementing new data access layer components unless they are new or specific to the plugin.

Here is an example of a domain entity in the plugin space:

using MainApp.Domain.Abstracts;

namespace Plugin.Domain  
{
    public class TestDomainEntity : IntegerIdentifiedEntity
    {
        public string Name { get; set; }
    }
}

And the API that provides access to it:

using System;  
using MainApp.Domain.Interfaces;  
using Plugin.Domain;

namespace Plugin.API.Testing  
{
    public class PluginTestApi : IPluginTestApi
    {
        private readonly IRepository<TestDomainEntity, Guid> testDomainEntityRepository;

        public PluginTestApi(
            IRepository<TestDomainEntity, Guid> testDomainEntityRepository
            )
        {
            this.testDomainEntityRepository = testDomainEntityRepository;
        }

        public void CreateTestDomainEntity()
        {
            var testDomainEntity = 
                new TestDomainEntity{Name = "New Test Entity"};

            this.testDomainEntityRepository.Save(testDomainEntity);
        }
    }
}

You can see here that we simply make use of whatever data access functionality is provided by the base application to store our entity because this plugin has no specific storage concerns.

Trouble

One issue that I ran into while using this method to plug functionality into a .Net Web API project was that the .Net routing code was not picking up my newly installed controller classes and was therefore telling me that those routes did not exist.

This can be solved by replacing the .Net default Controller Selector with one that uses the Windsor container to check which controllers are available:

public class WindsorControllerSelector : DefaultHttpControllerSelector  
{
    private readonly IWindsorContainer container;
    private readonly HttpConfiguration configuration;

    public WindsorControllerSelector(
    IWindsorContainer container, HttpConfiguration configuration
    ): base (configuration)
    {
        this.container = container;
        this.configuration = configuration;
    }

    public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
    {
        var controllerName = this.GetControllerName(request);

        var properControllerName = String.Format("{0}Controller", controllerName);

        var assignableHandlers = 
        this.container.Kernel.GetAssignableHandlers(typeof(ApiController));
        var controllerType = 
        assignableHandlers.Select(handler => handler.ComponentModel.Implementation)
            .FirstOrDefault(x => x.Name == properControllerName);

        if(controllerType == null)
        {
            return null;
        }

        return new HttpControllerDescriptor(this.configuration, controllerName, controllerType);
    }

    protected HttpControllerDescriptor GetDescriptor(Type controllerType)
    {
        var controllerName = controllerType.Name.Replace("Controller", String.Empty);

        return new HttpControllerDescriptor(this.configuration, controllerName, controllerType);
    }

    public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
    {
        var assignableHandlers = this.container.Kernel.GetAssignableHandlers(typeof(ApiController));
        var controllerTypes = assignableHandlers.Select(handler => handler.ComponentModel.Implementation).ToList();

        return controllerTypes.ToDictionary(x => x.Name.Replace("Controller", String.Empty), this.GetDescriptor);
    }
}

And this implementation is easily used in place of the default .Net Controller Selector with this bit of code in your Global.asax.cs file:

GlobalConfiguration.Configuration.Services.Replace(  
typeof(IHttpControllerSelector),  
new WindsorControllerSelector(container, GlobalConfiguration.Configuration)  
);

This will allow you to load in new controllers on the fly, even while the app is running.

NOTE: Another potential pitfall when adding plugins in this way is that you will either need to make sure that components in your plugin do not have the same names as components in your main app or that you add a specific name to each component in your plugin with something like this in your plugin installers:

container.Register(  
        Classes.FromThisAssembly().Pick()
            .WithService
            .DefaultInterfaces()
            .Configure(x => x.Named(String.Format("Plugin{0}", x.Name))
            )
        );

If you do not either name your components differently or include something like the above snippet you will run into component resolution problems at run time.

In summary, Castle Windsor provides a really nice, fairly straightforward way to plug functionality into your application in a well understood way at any level or as an entire vertical slice.