Castle Windsor lazy loading

I just committed a very cool feature to Castle Windsor/MicroKernel that adds lazy registration capabilities. By lazy registration I mean – you get a chance to register a component right at the spot when it’s about to be resolved.

This enables things like integration with external sources of components, like MEF, or WCF config files, lets you distribute your registration in time so that you don’t have to do all of it upfront and many more.

Behind all of this, is this interface:

/// <summary>

/// Provides lazy registration capabilities to the container.

/// </summary>

/// <remarks>

/// When a component is requested from a container, that has not been registered

/// container loads up all the implementers of this interface and asks them in turn

/// whethere they can provide that component, until it finds one that will.

/// </remarks>

public interface ILazyComponentLoader

{

    /// <summary>

    /// Used by container to allow the loader register component for given <paramref name="key"/> 

    /// and <paramref name="service"/> to the container at the time when it is requested

    /// </summary>

    /// <param name="key">Key of the requested component or null</param>

    /// <param name="service">Type of requested service or null</param>

    /// <returns>Registration that registers component for given key and/or service or null.</returns>

    /// <remarks>

    /// While both key and service can be null reference it is guaranteed that at least one of them will not be null.

    /// When implementer opts in to provide the requested component (by returning not-null registration) it is required

    /// to register component for requested key/service combination (when one of the elements is null, it should be ignored as well).

    /// When implementer does not want to register the requested component it nust return null.

    /// </remarks>

    IRegistration Load(string key, Type service);

}

You can now use it pretty easily, as I will show in a second. Let’s take a trivial example:

[DefaultImplementation(typeof(Implementation))]

public interface IHasDefaultImplementation

{

    void Foo();

}

 

public class Implementation : IHasDefaultImplementation

{

    public void Foo()

    {

        

    }

}

We have an interface, a class that implements it, and an attribute we put on the interface that points to its default implementation (assuming we have many). This example is completely artificial, but that’s not the point.

We still need some implementation of ILazyComponentLoader, that will work with this scenario.

public class Loader : ILazyComponentLoader

{

 

    public IRegistration Load(string key, Type service)

    {

        if (!Attribute.IsDefined(service, typeof(DefaultImplementationAttribute)))

        {

            return null;

        }

 

        var attributes = service.GetCustomAttributes(typeof(DefaultImplementationAttribute), false);

        var attribute = attributes[0] as DefaultImplementationAttribute;

        return Component.For(service).ImplementedBy(attribute.Implementation).Named(key);

    }

}

Our Loader is called by the container when a component is requested, that has not been registered in the container. We then check if type of the service requested has DefaultImplementationAttribute, and if it does we register the component with the default implementation, which then will be resolved.

To wire our loader with the container, we register it with ILazyComponentLoader service just like any other component. Yeah – no additional API to memorize!

var kernel = new DefaultKernel();

kernel.AddComponent<Loader>(typeof(ILazyComponentLoader));

That’s all we need to make this test pass:

[Test]

public void Can_Lazily_resolve_component()

{

 

    var service = kernel.Resolve("foo", typeof(IHasDefaultImplementation));

    Assert.IsNotNull(service);

    Assert.IsInstanceOf<Implementation>(service);

}

The code is available now in the trunk, and you can get latest binaries here.

Technorati Tags: