Windsor - IModelInterceptersSelector

time to read 4 min | 613 words

In my previous post I introduced the basis of context as an architectural pattern. Now I want to talk about how we can implement that using Windsor and a new extensibility point: IModelInterceptersSelector.

The interface is defined as:

/// <summary>
/// Select the appropriate interecptors based on the application specific
/// business logic
/// </summary>
public interface IModelInterceptorsSelector
{
    /// <summary>
    /// Select the appropriate intereceptor references.
    /// The intereceptor references aren't neccessarily registered in the model.Intereceptors
    /// </summary>
    /// <param name="model">The model to select the interceptors for</param>
    /// <returns>The intereceptors for this model (in the current context) or a null reference</returns>
    /// <remarks>
    /// If the selector is not interested in modifying the interceptors for this model, it 
    /// should return a null reference and the next selector in line would be executed (or the default
    /// model.Interceptors).
    /// If the selector return a non null value, this is the value that is used, and the model.Interectors are ignored, if this
    /// is not the desirable behavior, you need to merge your interceptors with the ones in model.Interecptors yourself.
    /// </remarks>
    InterceptorReference[] SelectInterceptors(ComponentModel model);

    /// <summary>
    /// Determain whatever the specified has interecptors.
    /// The selector should only return true from this method if it has determained that is
    /// a model that it would likely add interceptors to.
    /// </summary>
    /// <param name="model">The model</param>
    /// <returns>Whatever this selector is likely to add intereceptors to the specified model</returns>
    bool HasInterceptors(ComponentModel model);
}

And registering it in the container is simply:

container.Kernel.ProxyFactory.AddInterceptorSelector(selector);

Interceptors are the basis of AOP, but traditionally, you didn't get a lot of choices in how you compose your interceptors at runtime. Using IModelInterceptersSelector make it extremely easy to modify the selection of interceptors based on relevant business logic.

Let us take the following example. We have a warehouse service that we want to add caching to. However, we can't use the cache in the request comes from the fulfillment service. First, we define the caching interceptor, then, we define the logic that controls adding or removing it.

public class WarehouseCachingInterceptorSelector : IModelInterceptorsSelector
{
    public InterceptorReference[] SelectInterceptors(ComponentModel model)
    {
        if(model.Service!=typeof(IWarehouse))
            return null;
        if(Origin.IsFromFulfillment)
            return null;
        return new InterceptorReference[]{new InterceptorReference(typeof(WarehouseCachingInterceptor)), };
    }

    public bool HasInterceptors(ComponentModel model)
    {
        return model.Service == typeof (IWarehouse);
    }
}

And now we get caching for everything except for fulfillment. And we get this in a clean and very easy to understand way. :-D