Advanced Castle Windsor – generic typed factories, auto-release and more

This post is a play­ground for me, to try out some ideas I want to include in my talk about Wind­sor at KGD.NET meet­ing later this month.

Sce­nario

We have a mes­sag­ing appli­ca­tion built around two interfaces:

public interface Command

{

}

 

public interface Handler

{

    void Execute();

}

 

public interface Handler<T> : Handler where T : Command

{

    T Command { get; set; }

}

Hope­fully I don’t have to explain how they work. The idea is, appli­ca­tion receives com­mands from some­where, then it pulls all han­dlers reg­is­tered for this com­mand and let them han­dle the com­mand. Split of Han­dler inter­face into generic and non-generic part is there to make up for lack of co/contra-variance in .NET 3.5.

Com­mands and Handlers

Han­dlers are quite sim­ple classes imple­ment­ing closed ver­sion of Han­dler<> inter­face. For exam­ple to change client’s address we’d have the fol­low­ing 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);

    }

}

Noth­ing earth shat­ter­ing here. We would have sim­i­lar set up for other busi­ness events in the appli­ca­tion. We assume we can have more than one han­dler for sin­gle com­mand (for exam­ple another han­dler would update ship­ping costs and pro­mo­tions avail­able for the new address of the client).

The abil­ity to pull mul­ti­ple ser­vices from the con­tainer via typed fac­tory is not avail­able in Wind­sor 2.1.1 – you need the trunk ver­sion to take advan­tage of it.

Reg­is­tra­tion

To reg­is­ter the code we cre­ate 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 cou­ple inter­est­ing things here. First we reg­is­ter typed fac­tory facil­ity, which we’ll use later on to pull han­dlers for com­mands we receive. Then we reg­is­ter cus­tom selec­tor for typed fac­tory (dis­cussed below), and an inter­cep­tor (dis­cussed below). Then we  reg­is­ter all repos­i­to­ries and all han­dlers from given assem­bly, con­fig­ur­ing han­dlers with tran­sient lifestyle and with the inter­cep­tor we reg­is­tered above. Lastly we also reg­is­ter typed fac­tory for handlers:

public interface HandlerFactory

{

    Handler[] GetHandlersForCommand(Command command);

}

Typed fac­tory selector

The han­dler fac­tory has to do quite a lot of work for us. Given an instance of a com­mand, it has to pull from the con­tainer all the han­dlers for the command’s type. To do this we need to use a cus­tom selec­tor (and trunk ver­sion 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 cre­ate closed Han­dler<> type and return Typed­Fac­to­ryCompo­nent­Col­lec­tion (new type that pulls all com­po­nents for given ser­vice) pass­ing down the com­mand as typed argu­ment 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 sim­ple the call­ing code is. It has no knowl­edge of the (quite com­plex) process that takes place behind the scenes to cre­ate strongly typed instances of appro­pri­ate classes. It does not even know what actual type of com­mand it got.

What about releas­ing tran­sient handlers?

Isn’t this code too sim­ple though? It resolves han­dlers, which are tran­sient com­po­nents and it does not release them. And we all know tran­sient com­po­nents need to be released in Wind­sor, right?

Well – it does release them actu­ally, it just doesn’t do it explic­itly. Remem­ber we reg­is­tered an inter­cep­tor with all han­dlers. Here’s how that inter­cep­tor 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 inter­cep­tor releases the com­po­nent after the call to Exe­cute for us. Thanks to this we take the bur­den (no pun intended) of releas­ing the com­po­nents from the callers, and we make sure our han­dlers won’t leak mem­ory. This is quite a use­ful trick actu­ally, and while I have pre­cisely zero knowl­edge of NSer­vice­Bus, I think it could be used to fix the issue Davy dis­cussed, with­out hav­ing to mess with release policy.

  • Artur Doro­chow­icz

    Inter­est­ing. The thing with NSB is that it does han­dler dis­cov­ery and reg­is­tra­tion by itself, so I think one would need to recon­fig­ure mes­sage han­dlers in con­tainer with inter­cep­tor after NSB does its con­fig­u­ra­tion. Some­thing with Kernel.GetHandlers?, is this a good direction?

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    Well I was actu­ally think­ing about it as solu­tion in NSer­vice­Bus Cas­tle provider's code.

    From Castle's per­spec­tive I'd rather use IMod­elIn­ter­cep­torS­e­lec­tor and inject Releasing­In­ter­cep­tor for all Mes­sage­Han­dlers you resolve. This way is the most trans­par­ent to the rest of the code.

  • Artur Doro­chow­icz

    This is brilliant.

    I guess, I will never stop get­ting amazed by Windsor's awesomeness.

  • John Simons

    Not sure if you want to add ".WithService.Base()", because if you do, then classes that do not imple­ment Han­dler<> directly do not get inter­cepted!
    Also, I like more Han­dles<> instead of Han­dler<> :)

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    @John,

    Can you give me an exam­ple of the sce­nario you mentioned?

    pub­lic class Sub­Han­d­ler­Impl : Update­Client­Cor­re­spon­denceAd­dressHan­dler
    {
    pub­lic SubHandlerImpl(Repository<Client> clien­tRepos­i­tory) : base(clientRepository)
    {
    }
    }

    this class does not impl Han­dler<> directly, and it will be prop­erly resolved, called, inter­cepted and realeased.

  • http://www.colourcoding.net/blog Julian Birch

    Does it han­dle for­warded types, though? An nSer­vice­Bus saga would han­dle a lot of dif­fer­ent messages.

    I'm going to have to get my head around these improve­ments… after I fin­ish the build work.

  • John Simons

    Hmmm, that is inter­est­ing because I have sim­i­lar code to yours and when I added ".WithService.Base()" in the reg­is­tra­tion, I couldn't resolve Han­dlers that were not directly imple­ment­ing inter­face.
    So to fix it I removed that, and every­thing worked again.
    Any­way, this is an area that is quite dif­fi­cult to under­stand:
    .WithService.Base() vs .WithService.FromInterface() vs nothing

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    @Julian

    in v2.1.1 (and later) I'm pretty sure it does.
    in v2.0 it didn't due to a bug.

  • http://igorbrejc.net/ Igor Brejc

    Krzysztof,

    I've been using a sim­i­lar thing for some time now (I got the idea from Jeremy D. Miller's Even­tAg­gre­ga­tor post­ings). But in my case the han­dlers are long-running objects (like forms or con­trols), so I'm not doing any releas­ing imme­di­ately after the invocation.

    What hap­pens if you Release a sin­gle­ton like you did in the interceptor?

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    @Igor

    Noth­ing hap­pens. Or more pre­cisely — noth­ing you wouldn't expect. Sin­gle­ton (if it imple­mented IDis­pos­able) would be dis­posed when the con­tainer gets dis­posed, regard­less of whether you Release it or not.

  • http://it.tmod.pl/ Tomasz Mod­el­ski

    Krzysztof, please help & look at this ques­tion:
    stackoverflow.com/…/circular-interfaces-depen…

    prob­a­bly it's easy for you.

    [Polish:On].. i pomóż jak rodak rodakowi :-) … [Polish:Off]

  • John Simons

    Krzysztof,

    Why did you marked the inter­cep­tor class as Tran­sient?
    You don't have any state in that class.

    Cheers
    John

  • http://kozmic.pl/Default.aspx Krzysztof Koźmic

    John,

    There are two parts to the answer here.

    1. I had the state in my first take at it, but fac­tored it out.
    2. Inter­cep­tors should be tran­sient. You should have really good rea­son to make it otherwise.