/ asp.net

Building the Castle Windsor Dependency Injection Populator for ASP.NET vNext

As I blogged about previously, I built a Castle Windsor populator for ASP.NET vNext. For this post, I’ll walk through some of the more interesting pieces of it.

Installer

In Castle Windsor, an installer is a class that implements IWindsorInstaller and is used to register all components. It’s not necessary, but it encapsulates the registrations and allows for more organization if you have a ton of services to register. For the Castle Windsor populator, I set up a KServiceInstaller to register the components.

The KServiceInstaller has a constructor that takes IEnumerable<IServiceDescriptor>, which is the list of services to be registered.

public KServiceInstaller(IEnumerable<IServiceDescriptor> services)

When Install is called on the installer, it loops through each service and registers it with Windsor, using either an ImplementationType or ImplementationInstance.

ImplementationType vs ImplementationInstance

The IServiceDescriptor interface exposes ImplementationType and ImplementationInstance properties. If the type being registered is a singleton and has a specific instance that is already instantiated then the service descriptor include an ImplementationInstance. If the type will be instantiated by the DI container, then the service descriptor includes an ImplementationType. Performing a null check will allow you to figure out which one to use, and they should be registered with the Windsor Instance or ImplementedBy registration methods, respectively.

if (service.ImplementationType != null)
{
    container.Register(Component.For(service.ServiceType)
                                .ImplementedBy(service.ImplementationType)
                                .ConfigureLifeCycle(service.Lifecycle));
}
else
{
    container.Register(Component.For(service.ServiceType)
                                .Instance(service.ImplementationInstance)
                                .ConfigureLifeCycle(service.Lifecycle));
}

Lifestyle

There are 3 LifeCycles specified in the vNext DI framework:

public enum LifecycleKind
{
    Singleton,
    Scoped,
    Transient
}

These map to the LifestyleSingleton, LifestyleScoped and LifestyleTransient life cycles in Windsor. Singleton, obviously, means we have one and only one, use it everywhere. Transient means give me a new one every time I ask for one and Scoped means give me a new one per scope, and inside that scope use the same one everywhere. The scope set up here is per web request, so it creates a new one on each request and it is used for the life of the request.

A simple extension method makes it easy to configure lifecycles:

internal static ComponentRegistration<object> ConfigureLifeCycle(
    this ComponentRegistration<object> registration, LifecycleKind kind)
{
    switch (kind)
    {
        case LifecycleKind.Scoped:
            registration.LifestyleScoped();
            break;
        case LifecycleKind.Singleton:
            registration.LifestyleSingleton();
            break;
        case LifecycleKind.Transient:
            registration.LifestyleTransient();
            break;
    }

    return registration;
}

And it’s used as such:

container.Register(Component.For(service.ServiceType)
                            .ImplementedBy(service.ImplementationType)
                            .ConfigureLifeCycle(service.Lifecycle));

FallbackLazyComponentLoader

There are many services in the vNext framework that are instantiated and registered prior to registration of the MVC services and any custom services you use in your app. These aren’t known and won’t be registered by Windsor, so we’ll need to fallback to another IServiceProvider if a request comes in for the service. That’s where the FallbackLazyComponentLoader comes in.

Castle Windsor includes the ability to acquire components as they’re needed, on the spot without registering them first. This is exposed via the ILazyComponentLoader interface, which is what FallbackLazyComponentLoader implements. It exposes a single constructor taking an IServiceProvider:

public FallbackLazyComponentLoader(IServiceProvider provider)

If Windsor encounters a request for a service that is not registered, it will fallback and attempt to resolve it using this fallback service provider:

public IRegistration Load(string name, Type service, IDictionary arguments)
{
    var serviceFromFallback = _fallbackProvider.GetService(service);

    if (serviceFromFallback != null)
    {
        return Component.For(service).Instance(serviceFromFallback);
    }

    return null;
}

WindsorServiceProvider

To use a custom DI framework, you must register a new IServiceProvider. The Windsor populator returns a WindsorServiceProvider which can be registered with the framework:

private class WindsorServiceProvider : IServiceProvider
{
    private IWindsorContainer _container;

    public WindsorServiceProvider(IWindsorContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType)
    {
         return _container.Resolve(serviceType);
    }
}

When a request for a service comes in, Windsor will attempt to resolve it. If it isn’t explicitly registered it checks the fallback service provider, and if that fails it returns null.

comments powered by Disqus
David Zych

David Zych

Dave is a dad, husband, programmer, (amateur) photographer, half-Canadian, alumnus of CSUCI, and an overall nice guy.

Read More