Click here to Skip to main content
15,886,756 members
Articles / Programming Languages / C# 4.0

Aspect Oriented Programming (AOP) in C# using Castle DynamicProxy

Rate me:
Please Sign up or sign in to vote.
4.94/5 (9 votes)
8 Mar 2016CPOL5 min read 31.6K   452   17   2
This article explains how to use Castle DynamicProxy interceptors for implementing a decoupled logging architecture in C#

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.

 

Image 1

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.

Image 2

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'. 

C#
public interface IRocket
    {
        void Launch(int delaySeconds);
    }

Lets implement the interface by implementing the only required method 'Launch'.

C#
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.

C#
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.

C#
 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

C#
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.

C#
public class DependencyResolver
    {
        private static IWindsorContainer _container;

        //Initialize the container
        public static void Initialize()
        {
            _container = new WindsorContainer();
            _container.Register(new ComponentRegistration());
        }

        //Resolve types
        public static T For<T>()
        {
            return _container.Resolve<T>();
        }
    }

Tiny console application for running our code.

C#
 internal class Program
    {
        public  static void Main(string[] args)
        {
            //Initialize the dependency resolver
            DependencyResolver.Initialize();

            //resolve the type:Rocket
            var rocket = DependencyResolver.For<IRocket>();

            //method call
            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 !

Image 3

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
GeneralMy vote of 5 Pin
Rabban H.8-Mar-16 5:18
Rabban H.8-Mar-16 5:18 
GeneralRe: My vote of 5 Pin
Linjith Kunnon8-Mar-16 5:43
professionalLinjith Kunnon8-Mar-16 5:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.