Search code examples
castle-windsortyped-factory-facility

Windsor Generic Typed Factory


I'm trying to convert a factory class that get's a message of a certain type and resolves the processors for that message type to an implementation the Typed Factory Facility.

The code I'm starting from is this:

public interface IProcessor<in T> where T : Message
{
    void Process(T message);
}

public class ProcessorFactory : IProcessorFactory
{
    private readonly IKernel _container;

    public ProcessorFactory(IKernel container)
    {
        if (container == null) throw new ArgumentNullException("container");

        _container = container;
    }

    public void Process(Message message)
    {
        // Create a specific message processor type
        var processorType = typeof(IProcessor<>).MakeGenericType(message.GetType());

        // Resolve all processors for that message
        var processors = _container.ResolveAll(processorType);

        foreach (var processor in processors)
        {
            processor.GetType().GetMethod("Process").Invoke(processor, new[] { message });
        }
    }

public class ProcessorInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
                Component.For<IProcessorFactory>().ImplementedBy<ProcessorFactory>(),
                AllTypes.FromThisAssembly().BasedOn(typeof(IProcessor<>)).WithService.FirstInterface().
                    AllowMultipleMatches());
    }
}

Based on the following blogpost and documentation from Castle Windsor I created the following:

public interface IProcessor
{
    void Process();
}

public interface IProcessor<T> where T : Message
{
    /// <summary>
    /// Message to process
    /// </summary>
    T Message { get; set; }
}

public interface IProcessorFactory
{
    IProcessor[] GetAllProcessorsForMessage(Message message);
}

public class ProcessorInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<TypedFactoryFacility>()
            .Register(
                Component.For<ProcessorSelector, ITypedFactoryComponentSelector>(),
                Component.For<AutoReleaseProcessorInterceptor>(),

                AllTypes.FromThisAssembly()
                    .BasedOn(typeof(IProcessor<>))
                    .WithService.Base()
                    .Configure(c => c.LifeStyle.Is(LifestyleType.Transient)
                        .Interceptors<AutoReleaseProcessorInterceptor>()),

                Component.For<IProcessorFactory>().AsFactory(f => f.SelectedWith<ProcessorSelector>()));
    }
}

public class ProcessorSelector : DefaultTypedFactoryComponentSelector
{
    protected override Type GetComponentType(MethodInfo method, object[] arguments)
    {
        return typeof(IProcessor<>).MakeGenericType(arguments[0].GetType());
    }
}

When I call factory.GetAllProcessorsForMessage(new ExampleMessage()) I get the following error:

Unable to cast object of type 'Castle.Proxies.IProcessor`1Proxy' to type 'MyNamespace.Processors.IProcessor[]'.

What am I doing wrong and how can the code be improved?


Solution

  • That's simple. As the exception message is saying your proxies do not implement the non generic IProcessor interface. You need to make it a service on those components too.

    Also your selector tells Windsor what you're after is a single item whereas the signature implies you want a collection. Hence the cast fails because you're casting a single item to an array type.

    So your selector's method should have

    .MakeArrayType() at the end.