Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Castle Windsor apply interceptors with selector after component is already registered

I have an abstract base class which contains several methods, of which one I need to intercept at all times. The base class can be inherited by user defined classes and registered with the container on application startup. In the event user hasn't registered one, the container installer will register one itself.

Here's the problem - the user should not have to worry about adding the interceptor. Container should add it on its own, regardless of who and where registered the component.

This is what I'm doing now:

if(!container.Kernel.HasComponent(typeof(MyBaseComponent)))
    container.Register(Component.For<MyComponent>()
        .Interceptors(InterceptorReference
        .ForType<MyComponentMethodInterceptor>())
        .SelectedWith(new MyComponentMethodSelector()).AtIndex(1));

MyComponentMethodSelector is a simple IInterceptorSelector which checks whether the methodname equals the one I need to intercept (in which case a MyComponentMethodInterceptor gets added to it).

As you can see it will check whether the component is already registered first.

The question is - do I have a way of adding an interceptor if it IS already registered? The most obvious choice that comes to mind is using an IContributeComponentModelConstruction, however at that point I will run into not being able to select the method which the interceptor gets added to. Or is there?

EDIT:

I should have been a little more specific. I need to add an interceptor for a specific method only. Hence why I'm using a MyComponentMethodSelector. I am aware of IContributeComponentModel, and I started out with that until I realised there was no way for me to add a method selector.

like image 309
NeroS Avatar asked Jul 23 '14 07:07

NeroS


1 Answers

You can easily add configuration to a component with a ComponentModel construction contributor that is called at the construction of the component model for your service by implementing IContributeComponentModelConstruction.

Let's say you want to add an interceptor to any implementation of IEventuallyRegistered, and if the user didn't register a custom component you want to use DefaultRegistration:

public interface IEventuallyRegistered { void test(); }
public class DefaultRegistration : IEventuallyRegistered { public void test() { } }
public class EventuallyRegisteredInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation) { invocation.Proceed(); }
}

internal class Program
{
    private static void Main(string[] args)
    {
        var container = new Castle.Windsor.WindsorContainer();
        container.Register(Component.For<EventuallyRegisteredInterceptor>());
        container.Register(Component.For<IEventuallyRegistered>().ImplementedBy<DefaultRegistration>());
        // I'm not doing the optional registration, I just want to 
        // demonstrate upgrading a additional configuration

        var t = container.Resolve<IEventuallyRegistered>();
        t.test();
    }
}

Implementing the IContributeComponentModelConstruction interface lets you change the configuration of the component model when it is created:

public class RequireInterception : IContributeComponentModelConstruction
{
    public void ProcessModel(IKernel kernel, ComponentModel model)
    {
        if (model.Services.Contains(typeof(IEventuallyRegistered)))
        {
            model.Interceptors.Add(new InterceptorReference(typeof(EventuallyRegisteredInterceptor)));
        }
    }
}

Then add the component to the container before registering your component:

container.Kernel.ComponentModelBuilder.AddContributor(new RequireInterception());
container.Register(Component.For<IEventuallyRegistered>().ImplementedBy<DefaultRegistration>());

Even though it is possible to do it through the ComponentRegistered event of the kernel with a facility, this is not recommended; since it was included in the original answer I'm including it but I'm pushing it down. You would start by creating a facility

public class InterceptionFacility : AbstractFacility
{
    protected override void Init()
    {
        Kernel.ComponentRegistered += new Castle.MicroKernel.ComponentDataDelegate(Kernel_ComponentRegistration);
    }

    void Kernel_ComponentRegistration(string key, Castle.MicroKernel.IHandler handler)
    {
        if (typeof(IEventuallyRegistered).IsAssignableFrom(handler.ComponentModel.Implementation))
        {
            handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(EventuallyRegisteredInterceptor)));
        }
    }
}

Then add the facility to the container before the registration of your component, or your facility will miss the event when registering your component:

container.AddFacility<InterceptionFacility>();
container.Register(Component.For<IEventuallyRegistered>().ImplementedBy<DefaultRegistration>());
like image 72
samy Avatar answered Oct 27 '22 16:10

samy