Error Management Is Sometimes Exceptionally Difficult

May 11, 2011 | Castle Windsor

Update:  Please also read Part 2. The source is on github has been merged in Enterprise Library Contrib.

I tend to simplify exception handling at layer boundaries, when designing a distributed application. Most of the time if an exception is propagated there I try to apply some kind of policy in order to log the exception and then re-throw  a different exception.

When I started using the Exception Handling Application Block from Enterprise Library I found out that I had to also deploy the Unity assemblies.

The call below resolves the registered component for the ExceptionManager class:

EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();

The IServiceLocator.Current property of EnterpriseLibraryContainer class sets by default the Unity container (via UnityServiceLocator adapter class).

In fact, if I remove the Unity assemblies a FileNotFoundException is thrown, indicating that Microsoft.Practices.Unity assembly or one of its dependencies could not be found.

Since I use Windsor in that application I didn’t like the fact that I had to deploy it with two different IoC containers (!) so I started figuring out how I can call tell the EnterpriseLibraryContainer to resolve the ExceptionManager component using Windsor.

The first thing I did was to compile the Windsor adapter of the CommonServiceLocator against the current version of Windsor that I use (and also run the unit-tests and see if they pass).

Next, I set the IServiceLocator.Current property of the EnterpriseLibraryContainer class:

EntLibraryContainer.Current = new WindsorServiceLocator(container);

The last thing I did was to call the ConfigureContainer method on the EnterpriseLibraryContainer class. This method takes an IContainerConfigurator and an IConfigurationSource as parameters.

Great, so I had to provide a class implementing the IContainerConfigurator interface for the Windsor container.

The IContainerConfigurator interface contains a single method: 

void RegisterAll(
     IConfigurationSource configurationSource,        
     ITypeRegistrationsProvider rootProvider
     );

Below is the (currently incomplete) implementation:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel;

internal sealed class WindsorContainerConfigurator
    : IContainerConfigurator
{
    private readonly IWindsorContainer container;

    public WindsorContainerConfigurator(IWindsorContainer container)
    {
        this.container = container;
    }

    public void RegisterAll(
        IConfigurationSource configurationSource,
        ITypeRegistrationsProvider rootProvider)
    {
        foreach (TypeRegistration registration 
            in rootProvider.GetRegistrations(configurationSource))
        {
            Register(registration);
        }
    }

    private void Register(TypeRegistration registration)
    {
        Type implementation = registration.ImplementationType;

        if (!this.container.Kernel.HasComponent(implementation))
        {
            var componentRegistration =
                Component.For(registration.ServiceType)
                            .ImplementedBy(implementation);

            if (registration.ConstructorParameters.Any())
            {
                var dependencies = GetRegistrationDependencies(
                    componentRegistration, 
                    registration.ConstructorParameters);

                componentRegistration = 
                    componentRegistration.DependsOn(dependencies);
            }

            this.container.Register(AddLifeStyleToRegistration(
                componentRegistration, registration.Lifetime));
        }
    }

    private IDictionary GetRegistrationDependencies<TInterface>(
        ComponentRegistration<TInterface> registration,
        IEnumerable<ParameterValue> constructorParameters)
    {
        var dependencies = new Hashtable();

        foreach (ParameterValue pv in constructorParameters)
        {
            MemberExpression exp = pv.Expression
                as MemberExpression;

            if (exp != null)
            {
                string key = exp.Member.Name;
                object val = GetValue(exp);

                dependencies.Add(key, val);
            }
            else
            {
                // TODO: pv.Expression is a MethodCallExpression.
            }
        }

        return dependencies;
    }

    private static ComponentRegistration<TInterface> AddLifeStyleToRegistration<TInterface>(
        ComponentRegistration<TInterface> registration, 
        TypeRegistrationLifetime typeRegistrationLifetime)
    {
        if (typeRegistrationLifetime == 
            TypeRegistrationLifetime.Singleton)
        {
            registration = registration.LifeStyle.Singleton;
        }
        else if (typeRegistrationLifetime == 
            TypeRegistrationLifetime.Transient)
        {
            registration = registration.LifeStyle.Transient;
        }
        else
        {
            throw new ArgumentOutOfRangeException(
                "typeRegistrationLifetime", 
                "Only Transient and Singleton are supported.");
        }

        return registration;
    }

    private static object GetValue(Expression member)
    {
        var objectMember = Expression.Convert(member, typeof(object));
        var getterLambda = Expression.Lambda<Func<object>>(objectMember);

        return getterLambda.Compile().Invoke();
    }
}

I really don’t know what to do in the case of a MethodCallExpression as you can see inside the else block. However, after I can finish with the code inside that block I will be able to do this:

var container = new WindsorContainer();

// Add a SubResolver for components with IEnumerable<T> dependencies on .ctors.
container.Kernel.Resolver.AddSubResolver(
     new CollectionResolver(container.Kernel));

// This is the Windsor specific impl. of IContainerConfigurator interface.
var configurator = new WindsorContainerConfigurator(container);

// Configure the Enterprise Library Container to use Windsor internally.
EnterpriseLibraryContainer.ConfigureContainer(configurator, 
    ConfigurationSourceFactory.Create());

// Set Current property to a new instance of the WindsorServiceLocator adapter.
EnterpriseLibraryContainer.Current = new WindsorServiceLocator(container);

I will try to update the code as soon as I have done any changes.