Introduction
In this article, I will walk you through the Aspect-Oriented Programming (AOP) concepts in a .NET environment and how to create and attach aspects using Castle DynamicProxy. Before we get started, let me give you a quick intro on AOP and IoC. If you are already familiar with these concepts, you may skip this section completely.
What is AOP?
Aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. An aspect is a common functionality that's typically scattered across methods, classes and object hierarchies. A behavior that looks like it has structure but cant find a way to express it using traditional object oriented techniques.
A good example of aspect is logging which I will discuss in details in this article. Usually you write informative logs throughout your code base but logging is something that your class or object model really shouldn't care about as it doesn't represent a domain object.
Using AOP approach, we can create aspects for these cross cutting concerns and attach them to domain objects centrally using multiple techniques. IL code weaving and Interception are the widely used approaches. In this article I will take you through the process of creating and applying aspects dynamically using Castel Windsor framework.
Inversion of Control (IoC) / Dependency Injection (DI) Container
An IoC Container is a framework to create and inject dependencies automatically whenever required. DI Container helps us to manage dependencies within the application in a simple and more efficient way.
Most of the mainstream DI (Dependency Injection) containers have inbuilt support for interception. An advance technique using which you can intercept the method calls and alter the behavior of domain object during run time. We will leverage this feature for attaching aspects to our domain objects. My DI framework of choice is Castle Windsor and its DynamicProxy is one of the popular ways of applying aspects.
There are quite a lot of good articles in Code Project and different blogs which gives you more detailed information on this (IoC) topic. A detailed discussion on IoC is outside the scope of this article.
Interception using Castle DynamicProxy
Castle DynamicProxy is a library for generating .NET proxies during runtime. It allows you to dynamically alter and extend the behavior of your business objects. This makes your domain model more maintainable as cross cutting concerns are purely decoupled from the core domain model. Castle automatically creates the proxy if you specify interceptors for any component. You use interceptors to inject behavior into the proxy.
You may wonder how this whole thing works internally. Whenever the caller requests a business object (concrete class), IoC container resolves and wraps it inside a proxy object containing specified interceptors with the help of DynamicProxy. Container then returns the proxied object to the caller. Caller then interacts with the proxy directly. The proxy intercepts every method call to the business object and let the request flow through the interceptor pipeline.
Below diagram shows how the request flows inside a proxy. You can see that the request passes through all the interceptors before and after the actual method execution.
Steps for setting up Castle DynamicProxy in your project
- Download and Install ‘Castle.Windsor’ package from NuGet.
- Implement the IInterceptor interface. This is the interface that is going to be used by the DynamicProxy.
- Implement IRegistration interface and register your components. Register interceptors followed by the business components. Specify the interceptor to use with each business component.
- Create a static instance of Windsor container (IWindsorContainer), initialize it with the component registration info.
This is pretty much all it takes to configure Castle DynamicProxy!
Using the code
Another fine sunny day in Bangalore with gentle breeze. Weather conditions are just about right for launching a rocket! Lets start with our sample application. This app contains a business object 'Rocket' which we launch using a console app.
Interface contains a single method signature called 'Launch'.
public interface IRocket
{
void Launch(int delaySeconds);
}
Lets implement the interface by implementing the only required method 'Launch'.
public class Rocket: IRocket
{
public string Name { get; set; }
public string Model { get; set; }
public void Launch(int delaySeconds)
{
Console.WriteLine(string.Format("Launching rocket in {0} seconds",delaySeconds));
Thread.Sleep(1000 * delaySeconds);
Console.WriteLine("Congratulations! You have successfully launched the rocket");
}
}
Time to create our first Interceptor. We can do that by implementing the IInterceptor interface. This is the interface that is going to be used by the DynamicProxy.
As you may see below, we are logging on method entry, calls the invocation.Proceed() method which executes the actual method, logging on successful execution, logging on exception and logging on exit.We do not have to keep writing logging code inside our business model any more! We just need to attach LoggingInterceptor on to those components that require logging.
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var methodName = invocation.Method.Name;
try
{
Console.WriteLine(string.Format("Entered Method:{0}, Arguments: {1}", methodName, string.Join(",", invocation.Arguments)));
invocation.Proceed();
Console.WriteLine(string.Format("Sucessfully executed method:{0}", methodName));
}
catch (Exception e)
{
Console.WriteLine(string.Format("Method:{0}, Exception:{1}", methodName, e.Message));
throw;
}
finally
{
Console.WriteLine(string.Format("Exiting Method:{0}", methodName));
}
}
The IInvocation object which DynamicProxy exposes is very useful. It gives you access to current MethodInfo, Arguments, ReturnValue and many other details as you can see below.
public interface IInvocation
{
object[] Arguments { get; }
Type[] GenericArguments { get; }
object InvocationTarget { get; }
MethodInfo Method { get; }
MethodInfo MethodInvocationTarget { get; }
object Proxy { get; }
object ReturnValue { get; set; }
Type TargetType { get; }
object GetArgumentValue(int index);
MethodInfo GetConcreteMethod();
MethodInfo GetConcreteMethodInvocationTarget();
void Proceed();
void SetArgumentValue(int index, object value);
}
Implement IRegistration interface and register your components. Register interceptors followed by the business components. Specify the interceptor to use with each business component. As you may have noticed, LoggingInterceptor is attached to our only business component Rocket
public class ComponentRegistration : IRegistration
{
public void Register(IKernelInternal kernel)
{
kernel.Register(
Component.For<LoggingInterceptor>()
.ImplementedBy<LoggingInterceptor>());
kernel.Register(
Component.For<IRocket>()
.ImplementedBy<Rocket>()
.Interceptors(InterceptorReference.ForType<LoggingInterceptor>()).Anywhere);
}
}
Create a static instance of Windsor container (IWindsorContainer), initialize it with the component registration info.
public class DependencyResolver
{
private static IWindsorContainer _container;
public static void Initialize()
{
_container = new WindsorContainer();
_container.Register(new ComponentRegistration());
}
public static T For<T>()
{
return _container.Resolve<T>();
}
}
Tiny console application for running our code.
internal class Program
{
public static void Main(string[] args)
{
DependencyResolver.Initialize();
var rocket = DependencyResolver.For<IRocket>();
try
{
rocket.Launch(5);
}
catch (Exception ex)
{
}
System.Console.ReadKey();
}
}
Lets look at the console output. As you would expect, our LoggingInterceptor intercepted the method call and logged method entry and exit automatically. Thanks to DynamicProxy !
Points of Interest
This is an introductory article to make beginners and intermediate developers understand the basic concept of AOP using Castle Windsor DynamicProxy . In the coming days, I will keep updating this article and demonstrate how you can implement this solution using Log4net and DynamicProxy in a Web Api project.
History
First version - 8th March 2016