Self-Configuring Unity Interception

The application I'm currently working on performs user authorization using authorization objects injected into Service Layer methods using Unity Interface Interception. For a simplified example, the CustomerService Service Layer object implements ICustomerService:

public interface ICustomerService
{
    void UpdateCustomer(int customerId, string name);
}

...and has an authorization object injected into UpdateCustomer() method calls which checks that the user currently assigned to the thread is allowed to access the Customer with the given customerId.

As you'd imagine, there's quite a lot of these methods, which means quite a lot of authorization objects, and quite a lot of configuration. There's also other objects being injected into other method calls, so I decided (not for the first time) - I'll make them all configure themselves :)

As each of the authorization objects was already responsible for authorization, I gave the responsibility for organizing them to a separate AutoSetupInterceptionManager object. I made each of the authorization objects (and anything else which is executed via Interception) implement this interface:

public interface IAutoSetupInterceptionClient
{
    string TargetTypeAndMemberName { get; }

    void ExecuteBeforeMethodCall(string invokedMethodName, IMethodInvocation input);

    void ExecuteAfterMethodCall(string invokedMethodName, IMethodInvocation input, IMethodReturn methodReturn);
}

...then had the Manager find them all (using this extension method), pair them up with the method into which they should be injected, and register itself with Unity as an ICallHandler for that method. Whenever Unity passes an intercepted method call to the AutoSetupInterceptionManager, the Manager passes it - via the IAutoSetupInterceptionClient method - to whichever objects are configured for the method.

Hopefully that makes some kind of sense; if so - or if not - here's the AutoSetupInterceptionManager code. The auto-registration is done in the SetupAutoRegisteredInterception() method. The IMethodInvocation.GetMethodName() method is an extension method which returns the signature of the intercepted method in the form <class name>.<method name>.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;

internal class AutoSetupInterceptionManager : ICallHandler
{
    private readonly Dictionary<string, List<IAutoSetupInterceptionClient>> _interceptionClients;

    public AutoSetupInterceptionManager(IUnityContainer container)
    {
        this._interceptionClients = this.SetupAutoRegisteredInterception(container);
    }

    public int Order
    {
        get;
        set;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        string methodName = input.GetMethodName();

        bool interceptionClientsExist = this._interceptionClients.ContainsKey(methodName);

        if (interceptionClientsExist)
        {
            this._interceptionClients[methodName].ForEach(ic => ic.ExecuteBeforeMethodCall(methodName, input));
        }

        IMethodReturn methodReturn = getNext().Invoke(input, getNext);

        if (interceptionClientsExist)
        {
            this._interceptionClients[methodName].ForEach(ic => ic.ExecuteAfterMethodCall(methodName, input, methodReturn));
        }

        return methodReturn;
    }

    private Dictionary<string, List<IAutoSetupInterceptionClient>> SetupAutoRegisteredInterception(IUnityContainer container)
    {
        Type interceptionClientType = typeof(IAutoSetupInterceptionClient);

        IEnumerable<Type> allAvailableTypes = Assembly.GetExecutingAssembly().GetAvailableTypes();

        Type[] interfaceTypes = allAvailableTypes.Where(t => t.IsInterface).ToArray();

        Type[] interceptionClientTypes = allAvailableTypes
            .Where(t => !(t.IsInterface || t.IsAbstract) && interceptionClientType.IsAssignableFrom(t))
            .ToArray();

        var interceptionClients = new Dictionary<string, List<IAutoSetupInterceptionClient>>();

        interceptionClientTypes.ForEach(ict =>
        {
            IAutoSetupInterceptionClient interceptionClient = (IAutoSetupInterceptionClient)Activator.CreateInstance(ict);

            string interceptionInterfaceTypeAndMethodName = string.Concat("I" + interceptionClient.TargetTypeAndMemberName);

            Type interceptionInterfaceType = interfaceTypes
                .FirstOrDefault(t => interceptionInterfaceTypeAndMethodName.StartsWith(t.Name));

            string interceptionMethodName = interceptionInterfaceTypeAndMethodName.Replace(interceptionInterfaceType.Name, null);
            string interceptionTypeName = interceptionClient.TargetTypeAndMemberName.Replace(interceptionMethodName, null);

            string methodSignature = interceptionTypeName + "." + interceptionMethodName;

            if (!interceptionClients.ContainsKey(methodSignature))
            {
                interceptionClients.Add(methodSignature, new List<IAutoSetupInterceptionClient>());
            }

            if (!interceptionClients[methodSignature].Contains(interceptionClient))
            {
                container
                    .RegisterType(interceptionInterfaceType, new InterceptionBehavior<PolicyInjectionBehavior>())
                    .Configure<UnityInterception>()
                    .SetInterceptorFor(interceptionInterfaceType, new InterfaceInterceptor())
                    .AddPolicy(interceptionMethodName + "Interception")
                    .AddMatchingRule(new MemberNameMatchingRule(interceptionMethodName))
                    .AddCallHandler(this);
 
                interceptionClients[methodSignature].Add(interceptionClient);
            }
        });

        return interceptionClients;
    }
}

Print | posted @ Wednesday, April 20, 2011 5:38 PM

Comments on this entry:

No comments posted yet.

Post A Comment
Title:
Name:
Email:
Comment:
Verification: