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.