Upgrading to Windsor 2.5 (Northwind)

I was looking at Sharp Architecture project and as I went through the codebase (the sample application in particular) I found several spots that weren’t using Windsor in a optimal way, and few other that could really benefit from some of the new improvements in version 2.5. So instead of keeping that knowledge all to myself I though I might as well use it as an example and show the process of migration I went though with it. The guide is based on current source from SA repository, and its Northwind sample application.

So here we go

We start off by copying Windsor 2.5 binaries to bin folder of SharpArchitecture.

1_copy_binaries

Windsor 2.5 consists of only 2 assemblies, as compared to 4 in previous versions. Castle.MicroKernel.dll and Castle.DynamicProxy2.dll are no longer needed (the classes from these two assemblies were integrated into the two other assemblies).

2_remove_old_binaries

Since SharpArchitecture users NHibernate which has dependency on DynamicProxy I also needed to rebuild the NHibernate.ByteCode.Castle.dll for the new DynamicProxy (which now lives in Castle.Core.dll). It may seem complicated but really was just a matter of fixing some namespaces.

SharpArch project

Fixing Sharp Architecture was quite simple. I opened the solution, built it, watched it fail, and stared to fix references (removing references to defunct, Castle.DynamicProxy2.dll, and removing or replacing with Castle.Windsor.dll references to Castle.MicroKernel.dll)

Breaking change

While updating the code I also stumbled upon one breaking change in new Windsor.

3_breaking_change

ServiceSelector delegate (used in WithService.Select calls) changed signature so that its 2nd parameter is now an array of Types, not a single type. If you look at BreakingChanges.txt distributed with Windsor 2.5, you’ll find that it documents this breaking change along with suggestion how to upgrade your old code.

fix – depending on the scenario. You would either ignore it, or wrap your current method’s body
in foreach(var baseType in baseTypes)

In our case the former applies, so we just update the signature and move on. The project now compiles just fine.

Northwind project

With that done we can shift focus to the sample Northwind application. We don’t need to do anything other than upgrading references to Windsor, NHibernate bytecode provider and SharpArch to get it to compile. This does not mean that we’re done though.

Obsolete API

The project will compile but will give us 18 errors. That’s something most users upgrading older apps will see. If you take a look at the errors you’ll see something like this:

4_obsolete_api

In Windsor 2.5 all the old registration API (all AddComponent and friends methods) became obsolete, as first step towards cleaning the API of the container. All the obsolete methods points us towards alternative – supported API that we can use to achieve the same thing so we can quite easily migrate the old calls. We won’t follow the suggestions from the error messages. Instead we’ll take a step back, to look at how the registration is being done.

Installers

All the obsolete calls come from a single class – ComponentRegistrar, which looks like this:

public class ComponentRegistrar
{
	public static void AddComponentsTo(IWindsorContainer container)
	{
		AddGenericRepositoriesTo(container);
		AddCustomRepositoriesTo(container);
		AddApplicationServicesTo(container);
		AddWcfServiceFactoriesTo(container);
 
		container.AddComponent("validator",
								typeof (IValidator), typeof (Validator));
	}
	
	// private registration methods
}

Windsor has (and had for a very long time) a better – “official” way of doing this, using installers. To take advantage of that we’ll start by moving code from the Add*To methods, to dedicated installer types.

For example we could take the AddGenericRepositoriesTo method

  
private static void AddGenericRepositoriesTo(IWindsorContainer container)
{
	container.AddComponent("repositoryType",
							typeof (IRepository<>), typeof (Repository<>));
	container.AddComponent("nhibernateRepositoryType",
							typeof (INHibernateRepository<>), typeof (NHibernateRepository<>));
	container.AddComponent("repositoryWithTypedId",
							typeof (IRepositoryWithTypedId<,>), typeof (RepositoryWithTypedId<,>));
	container.AddComponent("nhibernateRepositoryWithTypedId",
							typeof (INHibernateRepositoryWithTypedId<,>), typeof (NHibernateRepositoryWithTypedId<,>));
}

and extract it to an installer:

public class GenericRepositoriesInstaller : IWindsorInstaller
{
	public void Install(IWindsorContainer container, IConfigurationStore store)
	{
		container.Register(
			Add(typeof (IRepository<>), typeof (Repository<>)),
			Add(typeof (INHibernateRepository<>), typeof (NHibernateRepository<>)),
			Add(typeof (IRepositoryWithTypedId<,>), typeof (RepositoryWithTypedId<,>)),
			Add(typeof (INHibernateRepositoryWithTypedId<,>), typeof (NHibernateRepositoryWithTypedId<,>)));
	}
 
	private IRegistration Add(Type service, Type implementation)
	{
		return Component.For(service).ImplementedBy(implementation);
	}
}

I dropped the name of the component since it’s never used in the application anyway, and I extracted common code to a helper method.

Factories and parameters

Let’s have a look at another one of these methods:

  
private static void AddWcfServiceFactoriesTo(IWindsorContainer container)
{
	container.AddFacility("factories", new FactorySupportFacility());
	container.AddComponent("standard.interceptor", typeof (StandardInterceptor));
 
	var factoryKey = "territoriesWcfServiceFactory";
	var serviceKey = "territoriesWcfService";
 
	container.AddComponent(factoryKey, typeof (TerritoriesWcfServiceFactory));
	var config = new MutableConfiguration(serviceKey);
	config.Attributes["factoryId"] = factoryKey;
	config.Attributes["factoryCreate"] = "Create";
	container.Kernel.ConfigurationStore.AddComponentConfiguration(serviceKey, config);
	container.Kernel.AddComponent(serviceKey, typeof (ITerritoriesWcfService),
									typeof (TerritoriesWcfServiceClient), LifestyleType.PerWebRequest);
}

There’s quite a lot going on here. It’s using FactorySupportFacility to register a service as well as a factory that will provide instances of this service. Why does it use a factory?

  
public class TerritoriesWcfServiceFactory
{
	public ITerritoriesWcfService Create()
	{
		var address = new EndpointAddress(
			// I see the below as a magic string; I typically like to move these to a 
			// web.config reader to consolidate the app setting names
			ConfigurationManager.AppSettings["territoryWcfServiceUri"]);
		var binding = new WSHttpBinding();
 
		return new TerritoriesWcfServiceClient(binding, address);
	}
}

The factory provides arguments to the service, and one of these arguments is also depending on a value from the config file. How can we do this better? We have two options here. We either register the binding and endpoint address in our container as services, so that Windsor itself can provide them to the TerritoriesWcfServiceClient or we register them as inline dependencies of the service. I don’t think it makes much sense to register them as services, so we’ll go with the latter option.

public class TerritoriesWcfServiceClientInstaller:IWindsorInstaller
{
	public void Install(IWindsorContainer container, IConfigurationStore store)
	{
		var address = new EndpointAddress(ConfigurationManager.AppSettings["territoryWcfServiceUri"]);
		container.Register(Component.For<ITerritoriesWcfService>()
							.ImplementedBy<TerritoriesWcfServiceClient>()
							.DependsOn(Property.ForKey<Binding>().Eq(new WSHttpBinding()),
										   Property.ForKey<EndpointAddress>().Eq(address))
							.LifeStyle.PerWebRequest);
	}
}

We’re not using a factory here, letting Windsor create the instance for us. We’re also passing the binding and address as typed dependencies, which is a new option in version 2.5.

The last method I want to look at (as this post is already enormously big) is this:

  
private static void AddApplicationServicesTo(IWindsorContainer container)
{
	container.Register(
		AllTypes.Pick()
			.FromAssemblyNamed("Northwind.ApplicationServices")
			.WithService.FirstInterface());
}

There are two issues with it. First it calls Pick() before FromAssembly*. That’s not a big deal but in Windsor 2.5 we tried to unify the API so that the order always should be: Specify assembly –> specify components –> configure.

The other issue is that it uses FirstInterface to pick a service for a type. Problem with that is, that if type implements more than one interface, which one is “first” is undefined. It can be one on Thursdays, and the other one on Fridays. Good luck chasing issues caused by this.

Default service

Windsor 2.5 adds new option that first the purpose much better in this case – default interface. It performs matching based on type/interface name. Since we have actually just single class and interface in that assembly: DashboardService/IDashboardService they are perfect match for this. So that our installer would look like this:

  
public class ApplicationServicesInstaller : IWindsorInstaller
{
	public void Install(IWindsorContainer container, IConfigurationStore store)
	{
		container.Register(
			AllTypes.FromAssemblyNamed("Northwind.ApplicationServices")
			.Pick().WithService.DefaultInterface());
	}
}

Installing the installers

Now having all registration enclosed in installers in our project we can change this:

ComponentRegistrar.AddComponentsTo(container);

to this:

container.Install(FromAssembly.This());

and Windsor will take care of all the rest.

In closing

That’s pretty much all it takes to upgrade the app to run on top of latest and greatest version of Windsor. In addition we introduced some new features that you likely are going to take advantage of when upgrading your apps (or starting new ones as well). I suspect there is some room for improvement in the Northwind app, and place for some other Windsor features but that perhaps should be left for another post.

  • Valeriu Caraulean

    Noticed a funny thing – in Castle where marked obsolete methods like
    Container.AddComponent(Type, Type)

    But one of your installers contains this fragment:
    private IRegistration Add(Type service, Type implementation)
    {
    return Component.For(service).ImplementedBy(implementation);
    }

    Moreover, similar convenience code I have in our projects.

    Now the question, is API missing something?
    Yes it’s cleaner, but what’s the benefit if people are writing anyway shortcuts and extension methods?

  • @Valeriu

    I’m glad you asked.

    In short, the fluent API is much more flexible, so instead of doing tricks like in AddWcfServiceFactoriesTo method you can achieve the same through the fluent API instead of tunneling it under the radar. The new API is also extensible, so you can easily add your own semantics to it (like facilities do).

    Also if the project had been a little bit better structured you could use that to your advantage and register the types via convention.

    Not to mention that Windsor has some performance optimizations towards usage of the fluent API (and in future version will have even more).

    So instead of keeping the old inferior API, it’s better to make it obsolete to guide people towards the other, better approach.

  • John Simons

    Krzysztof,

    Valeriu observation is valid.
    The point Valeriu is trying to make is that you ended up creating a method with the same signature of the one you deprecated, so if you find the helper method shorter and easier to use why did you deprecate it on the framework?

    Cheers
    John

  • @John,

    The old API got deprecated for the following reasons:
    1. to unify the API – so that we don’t have multiple ways of doing the same thing.
    2. fluent API is much more powerful, flexible and extensible, see the AddWcfServiceFactoriesTo example
    3. Windsor is optimized for fluent API (performance wise).

  • John Simons

    It would be awesome if you could also review NServiceBus usage of Windsor 🙂