This post is a playground for me, to try out some ideas I want to include in my talk about Windsor at KGD.NET meeting later this month.
Scenario
We have a messaging application built around two interfaces:
public interface Command
{
}
public interface Handler
{
void Execute();
}
public interface Handler<T> : Handler where T : Command
{
T Command { get; set; }
}
Hopefully I don’t have to explain how they work. The idea is, application receives commands from somewhere, then it pulls all handlers registered for this command and let them handle the command. Split of Handler interface into generic and non-generic part is there to make up for lack of co/contra-variance in .NET 3.5.
Commands and Handlers
Handlers are quite simple classes implementing closed version of Handler<> interface. For example to change client’s address we’d have the following command
[Serializable]
public class UpdateClientCorrespondenceAddressCommand : Command
{
private readonly AddressDto address;
private readonly Guid clientId;
public UpdateClientCorrespondenceAddressCommand(Guid clientId, AddressDto address)
{
this.clientId = clientId;
this.address = address;
}
public AddressDto Address
{
get { return address; }
}
public Guid ClientId
{
get { return clientId; }
}
}
and its handler:
public class UpdateClientCorrespondenceAddressHandler : Handler<UpdateClientCorrespondenceAddressCommand>
{
private readonly Repository<Client> clientRepository;
public UpdateClientCorrespondenceAddressHandler(Repository<Client> clientRepository)
{
this.clientRepository = clientRepository;
}
public UpdateClientCorrespondenceAddressCommand Command { get; set; }
public void Execute()
{
var command = Command;
if (command == null) return;
var client = clientRepository.Get(command.ClientId);
client.ChangeCorrespondenceAddress(command.Address);
clientRepository.Update(client);
}
}
Nothing earth shattering here. We would have similar set up for other business events in the application. We assume we can have more than one handler for single command (for example another handler would update shipping costs and promotions available for the new address of the client).
The ability to pull multiple services from the container via typed factory is not available in Windsor 2.1.1 – you need the trunk version to take advantage of it.
Registration
To register the code we create an installer:
public class Installer : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<TypedFactoryFacility>()
.Register(
Component.For<ITypedFactoryComponentSelector>().ImplementedBy<HandlerSelector>(),
Component.For<AutoReleaseHandlerInterceptor>(),
AllTypes.FromAssemblyContaining<Program>()
.BasedOn(typeof(Repository<>))
.WithService.Base()
.Configure(c => c.LifeStyle.Singleton)
.BasedOn(typeof(Handler<>))
.WithService.Base()
.Configure(c => c.LifeStyle.Is(LifestyleType.Transient)
.Interceptors<AutoReleaseHandlerInterceptor>()),
Component.For<HandlerFactory>().AsFactory());
}
}
There are a couple interesting things here. First we register typed factory facility, which we’ll use later on to pull handlers for commands we receive. Then we register custom selector for typed factory (discussed below), and an interceptor (discussed below). Then we register all repositories and all handlers from given assembly, configuring handlers with transient lifestyle and with the interceptor we registered above. Lastly we also register typed factory for handlers:
public interface HandlerFactory
{
Handler[] GetHandlersForCommand(Command command);
}
Typed factory selector
The handler factory has to do quite a lot of work for us. Given an instance of a command, it has to pull from the container all the handlers for the command’s type. To do this we need to use a custom selector (and trunk version of Windsor).
public class HandlerSelector:ITypedFactoryComponentSelector
{
public TypedFactoryComponent SelectComponent(MethodInfo method, Type type, object[] arguments)
{
Debug.Assert(arguments.Length == 1);
var message = arguments[0];
var handlerType = typeof(Handler<>).MakeGenericType(message.GetType());
return new TypedFactoryComponentCollection(handlerType.MakeArrayType(), new Arguments(arguments));
}
}
Based on the command’s type we create closed Handler<> type and return TypedFactoryComponentCollection (new type that pulls all components for given service) passing down the command as typed argument to the resolution.
Putting it all together
We can now use the code like this:
private static void Main()
{
using(var container = new WindsorContainer().Install(new Installer()))
{
var factory = container.Resolve<HandlerFactory>();
DoActualWork(factory);
Console.ReadKey(true);
}
}
private static void DoActualWork(HandlerFactory factory)
{
var command = ImmitateCommandArrived();
var handlers = factory.GetHandlersForCommand(command);
foreach (var handler in handlers)
{
handler.Execute();
}
}
private static Command ImmitateCommandArrived()
{
return new UpdateClientCorrespondenceAddressCommand(Guid.NewGuid(), GetSomeAddress());
}
Notice how simple the calling code is. It has no knowledge of the (quite complex) process that takes place behind the scenes to create strongly typed instances of appropriate classes. It does not even know what actual type of command it got.
What about releasing transient handlers?
Isn’t this code too simple though? It resolves handlers, which are transient components and it does not release them. And we all know transient components need to be released in Windsor, right?
Well – it does release them actually, it just doesn’t do it explicitly. Remember we registered an interceptor with all handlers. Here’s how that interceptor looks like:
[Transient]
public class AutoReleaseHandlerInterceptor : IInterceptor
{
private static readonly MethodInfo Execute = typeof(Handler).GetMethod("Execute");
private readonly IKernel kernel;
public AutoReleaseHandlerInterceptor(IKernel kernel)
{
this.kernel = kernel;
}
public void Intercept(IInvocation invocation)
{
if(invocation.Method!=Execute)
{
invocation.Proceed();
return;
}
try
{
invocation.Proceed();
}
finally
{
kernel.ReleaseComponent(invocation.Proxy);
}
}
}
The interceptor releases the component after the call to Execute for us. Thanks to this we take the burden (no pun intended) of releasing the components from the callers, and we make sure our handlers won’t leak memory. This is quite a useful trick actually, and while I have precisely zero knowledge of NServiceBus, I think it could be used to fix the issue Davy discussed, without having to mess with release policy.