I’m developing an application where a great deal of the functionality will be provided through plug in assemblies dropped into the main installation folder. This application is using Ninject as the dependency injection framework, and Caliburn.Micro for its support of the MVVM architecture.

Fortunately, making these two frameworks cooperate is pretty easy - all you have to do is to create a custom Caliburn.Micro bootstrapper to tie them together. Here’s mine, with some explanation about how it works interspersed.

public class AppBootstrapper : Bootstrapper<IShell>
{        
    protected override void Configure()
    {
        mKernel = new StandardKernel();
        mKernel.Load(Assembly.GetExecutingAssembly());
        mKernel.Load("*.dll");

The Load() method loads all the assemblies in the same directory as the main application and automatically registers any public classes implementing INinjectModule.

Tip: Test to see if a module is being loaded by putting a breakpoint in it’s Load() method. Use DebuggerBreak() if you can’t do this in the IDE. If the module isn’t being loaded, double check that it’s public.

    var assemblies 
        = mKernel.GetModules()
            .Select(m => m.GetType().Assembly)
            .Distinct()
            .ToList();
    AssemblySource.Instance.AddRange(assemblies);
}

When Caliburn.Micro looks for a View to display for a given ViewModel, it does not scan all loaded assemblies, but only those listed by the AssemblySource. If we want the Views in our plug in assemblies to be found, we need to add them to this list.

For simplicity, I assume that every assembly containing Views will also have a Ninject module. This allows me to reuse the plugin discovery already performed by the Load() call above by taking all the loaded modules, finding their original assembly and using them for Caliburn.Micro.

Tip: If your View isn’t being found by Caliburn.Micro and its declared in some other assembly - not the entry point of your application - make sure your bootstrapper adds the appropriate assembly to the module.

Once our module discovery and assembly registration steps are complete, the rest of the bootstrapped is nearly trivial.

    protected override object 
        GetInstance(Type serviceType, string key)
    {
        return mKernel.Get(serviceType, key);
    }

    protected override IEnumerable<object>
        GetAllInstances(Type serviceType)
    {
        return mKernel.GetAll(serviceType);
    }

    private IKernel mKernel;
}

Comments

blog comments powered by Disqus