Unity is a well known dependency injection container. Custom extensions can be created to extend its functionality. The download of Unity contains an extension for interception.
In this post I will show you, how interception can be used for separation of cross-cutting concerns.
Introduction
Interception is one possibility to support Aspect-oriented programming (AOP). It is useful to keep your business classes clean from other concerns like logging or caching.
There are several possibilities to use AOP in .NET. One option is to use a post-compiler like PostSharp. PostSharp modifies the IL code after compilation and injects aspects into the code.
In contrary to that, interception is performed at runtime, which implicates some limitations. Depending on the interceptor the following restrictions apply:
- Transparent Proxy Interceptor: requires interface or needs to inherit from MarshalByRefObject
- Interface Interceptor: requires interface
- Virtual Method Interceptor: only on virtual methods
How does interception work?
When you request the target object from the Unity container, you won't get an instance of the class you have configured. In fact you will get a dynamically generated proxy object or a derived class.
If you call a method on the proxy object you have the possibility to execute code before and after the method call is delegated to the 'real' object instance. Your custom code has to be placed in a class/behavior that implements the ICallHandler interface. Within the behavior you have access to the arguments of the method call, you are able to swallow exceptions or return custom exceptions.
By the way: It is also possible to use Unity interception without an Unity container.
Example: Logging of exceptions and method arguments
In the following example we will create two custom behaviors, which both implement the ICallHandler interface:
- LoggerCallHandler: Logs method arguments
- ExceptionLoggerCallHandler: Logs method arguments and stacktrace when an exception occurs
The ExceptionLoggerCallHandler:
internal class ExceptionLoggerCallHandler : ICallHandler { public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { IMethodReturn result = getNext()(input, getNext); if (result.Exception != null) { Console.WriteLine("Exception occured: " + result.Exception.Message);Console.WriteLine("Parameters:"); foreach (var parameter in input.Arguments) { Console.WriteLine(parameter.ToString()); } Console.WriteLine("StackTrace:"); Console.WriteLine(Environment.StackTrace); } return result; } public int Order { get; set; }
}
To apply a behavior to a method, you have to create a corresponding HandlerAttribute which creates an instance of the behavior:
internal class ExceptionLoggerAttribute : HandlerAttribute { public override ICallHandler CreateHandler(IUnityContainer container) { return new ExceptionLoggerCallHandler(); } }
In this example we want to create a simple calculator. To use Interface Interception we have to create a interface were we can apply the behaviors:
public interface ICalculator { [Logger] int Add(int first, int second); [ExceptionLogger] int Multiply(int first, int second); }
The implementation of the calculator stays the same. Now we have to setup the Unity container:
IUnityContainer container = new UnityContainer(); container.AddNewExtension<Interception>(); container.RegisterType<ICalculator, Calculator>() .Configure<Interception>() .SetInterceptorFor<ICalculator>(new InterfaceInterceptor()); // Resolve ICalculator calc = container.Resolve<ICalculator>(); // Call method calc.Add(1, 2);
When you call the Resolve method on the container you will not get an instance of the Calculator class, you will get an proxy class. Its name will be something like:
'DynamicModule.ns.Wrapped_ICalculator_50fa0b711518484a9ca19e731132c334'.
When you call one of the methods on the wrapper class, the CallHandlers get executed.
Conclusion
Unity does not provide a full AOP framework, since you have several limitation. Nevertheless interception can be really useful to fulfill basic AOP requirements.