Interception in .NET with Castle Windsor

Vishvvas
Posted by Co Author(s): Shweta Haridas in .NET Framework category on for Advance level | Points: 250 | Views : 2878 red flag
Rating: 5 out of 5  
 1 vote(s)

Interception with Castle Windsor is one of many options to achieve interception in .NET and it could be good candidate for employing interception for .NET framework based applications.
Recommendation
Read Interception in .NET before this article.

Introduction & Background

In the previous article (http://www.dotnetfunda.com/articles/show/3025/interception-in-net) we discussed about unity interception and how cross cutting concerns can be handled by using unity interception extension. Apart from Unity, there are other interception mechanisms like Castle Windsor, Ninject which can be used for AOP. This article explores the usage of Castle Windsor for handling cross-cutting concerns. Castle Windsor helps to build loosely coupled modules in applications. The basis of castle is the ability to create a component that is able to intercept every call to methods and or properties of an interface.


Objective

To learn about interception through Castle Windsor and how it can be employed to achieve the cross cutting concerns.


Description

Castle Windsor is fundamentally an IOC i.e. inversion of control container which helps make application code loosely coupled. The aspects or behaviors like logging, caching, exception handling can be applied to the classes externally without modifying the existing source code which helps build a better designed software. Castle Windsor can intercept virtual methods, interface methods and properties though it can’t intercept the inner methods. 

1.   Code Implementation 

 Install Windsor: Castle Windsor is Inversion of Control container available for .NET and Silverlight. The latest version that will be installed after following command will be 3.3.0. Open Package Manager Console.
Install-Package Castle.Windsor
Main method and methods called from main method
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using InterceptionWithCastleWindsor.InterfaceInterception;
using InterceptionWithCastleWindsor.VirtualMethodInterception;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InterceptionWithCastleWindsor
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("/****** " + " Interception with Castle Windsor " + "******/");
            Console.WriteLine("/****** " + " Enter the choice for interception 1. Interface Interception 2. Virtual Method Interception " + "******/");
            int value = int.Parse(Console.ReadLine());
            InterceptionWithCastleWindsor(value);

        }

        private static void InterceptionWithCastleWindsor(int value)
        {
            switch (value)
            {
                case 1:
                    RegisterContainerForInterfaceInterception();
                    GetUserChoiceInput();
                    break;
                case 2:
                    RegisterContainerForVirtualMethodInterception();
                    GetUserChoiceInput();
                    break;
                default:
                    Console.WriteLine("Invalid user input");
                    Console.WriteLine("Default choice for Interface Interception");
                    RegisterContainerForInterfaceInterception();
                    Console.ReadLine();
                    break;

            }

        }
        /// 

        /// This method is used to take choice from user whether to continue or exit 
        /// 

        private static void GetUserChoiceInput()
        {

            bool validInput = false;
            while (!validInput)
            {
                Console.WriteLine("/****** " + " Press Y if you want to continue and N if you want to exit. " + "******/");
                string val = Console.ReadLine().ToString();
                if (val.Equals("N"))
                {
                    validInput = true;
                    Console.WriteLine("Press any key to exit");
                    Console.ReadKey();

                }
                else if (val.Equals("Y"))
                {
                    validInput = true;
                    Console.WriteLine("/****** " + " Enter the choice for interception 1. Interface Interception 2. Virtual Method Interception " + "******/");
                    int choice = int.Parse(Console.ReadLine());
                    InterceptionWithCastleWindsor(choice);

                }
                else
                {
                    validInput = false;
                    Console.WriteLine("Invalid user input..");

                }
            }


        }

        /// 

        /// This method is used to register windsor container for Interface interception
        /// 

        private static void RegisterContainerForInterfaceInterception()
        {
            Console.WriteLine("/****** " + " Interface Interception with Castle Windsor " + "******/");
            using (var container = new WindsorContainer())
            {
                container.Register(
                Component.For().ImplementedBy().Interceptors(),
                Component.For().LifeStyle.Transient);
                Console.WriteLine("/****** " + " Components are registered in WindsorContainer " + "******/");
                var service = container.Resolve(); // container.Resolve() method creates dynamic proxy.
                Console.WriteLine("/****** " + " Enter the parameters to be intercepted " + "******/");
                Console.Write("Please enter parameter 1 : ");
                int val1 = Convert.ToInt32(Console.ReadLine());
                Console.Write("Please enter parameter 2 : ");
                int val2 = Convert.ToInt32(Console.ReadLine());
                service.Add(val1, val2);
                service.Multiply(val1, val2);
            }

        }

        /// 

        /// This method is used to register windsor container for Virtual Method interception
        /// 

        private static void RegisterContainerForVirtualMethodInterception()
        {
            Console.WriteLine("/****** " + " Virtual Method Interception with Castle Windsor " + "******/");
            using (var container = new WindsorContainer())
            {
                container.Register(
                Component.For().ImplementedBy().Interceptors(),
                Component.For().LifeStyle.Transient);
                Console.WriteLine("/****** " + " Components are registered in WindsorContainer " + "******/");
                var service = container.Resolve(); // container.Resolve() method creates dynamic proxy.
                Console.WriteLine("/****** " + " Enter the parameters to be intercepted " + "******/");
                Console.Write("Please enter parameter 1 : ");
                int val1 = Convert.ToInt32(Console.ReadLine());
                Console.Write("Please enter parameter 2 : ");
                int val2 = Convert.ToInt32(Console.ReadLine());
                service.Add(val1, val2);
                service.Multiply(val1, val2);
            }
        }
    }
}

        2. Business logic:  Add interface and its implementation.

Code for Interface interception

Interface:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InterceptionWithCastleWindsor.InterfaceInterception
{
    public interface ICalculatorWithInterfaceInterception
    {
        void Add(int number1, int number2);

        void Multiply(int number1, int number2);
    }
}

Implementation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InterceptionWithCastleWindsor.InterfaceInterception
{
    class CalculatorWithInterfaceInterception : ICalculatorWithInterfaceInterception
    {
        public void Add(int number1, int number2)
        {
            int result = number1 + number2;
            Console.WriteLine("In Add method");
            Console.WriteLine("Result of addition:" + result);
            Multiply(2, 4);  // inner method does not get intercepted with castle windsor.
        }

        public void Multiply(int number1, int number2)
        {
            int result = number1 * number2;
            Console.WriteLine("In multiply method");
            Console.WriteLine("Result of multiplication:" + result);
        }
    }
} 
 

3. Code for VirtualMethod interception:

Interface:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InterceptionWithCastleWindsor.VirtualMethodInterception
{
    public interface ICalculatorWithVirtualMethodInterception
    {
        void Add(int number1, int number2);

        void Multiply(int number1, int number2);
    }
}
Implementation 

using System;
using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InterceptionWithCastleWindsor.VirtualMethodInterception { class CalculatorWithVirtualMethodInterception : ICalculatorWithVirtualMethodInterception { public virtual void Add(int number1, int number2) { int result = number1 + number2; Console.WriteLine("In Add method"); Console.WriteLine("Result of addition:" + result); Multiply(2, 4); } public virtual void Multiply(int number1, int number2) { int result = number1 * number2; Console.WriteLine("In multiply method"); Console.WriteLine("Result of multiplication:" + result); } } }

      4. Add logic for interception i.e. behaviors to be applied. Here logging behavior is applied which logs method arguments and method signature. Castle Windsor uses “Intercept()” method of interface “IInterceptor” to intercept method arguments as shown below.  “Invocation.Proceed()” method is used to call next method in the chain. Whenever the methods to which behaviors are applied are called, the call first goes to “Intercept()” method.

     Castle Interceptor

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Castle.DynamicProxy;

namespace InterceptionWithCastleWindsor
{
    class CastleInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            Console.WriteLine("Intercepted method: " + invocation.Method);
            invocation.Proceed();
            Console.WriteLine("Intercepted method parameters are :");
            foreach (var parameter in invocation.Arguments)
            {
                Console.WriteLine(parameter.ToString()); // logs the parameters of the intercepted methods on console
            }
            Console.WriteLine();

        }
    }
}

 5.Output: The output shows that the methods which are in interface(virtual and non-virtual) gets intercepted whereas internal methods do not get intercepted with Castle Windsor.



Summary And Conclusion

The Castle Windsor is the simpler way by which AOP concerns can be achieved. The logging of method parameters and method signature is demonstrated in this discussion. This can be achieved for the methods declared in interface (virtual and non-virtual). The type    to be intercepted for both (methods in interface and virtual methods) will be same i.e. CastleInterceptor (inherited from Iinterceptor ) unlike Unity where type to be intercepted has to be mentioned.

Such mechanism not only provides ability to intercept but also simplifies the implementation of crosscutting concerns through AOP. The code modules or API for crosscutting concerns can be easily replaced by such mechanism in turn minimizing the efforts, investments and  also providing consistent interface.


Reference

http://msdn.microsoft.com/en-us/library/aa973811.aspx

http://docs.castleproject.org/Windsor.Basic-Windsor-Tutorial.ashx


Page copy protected against web site content infringement by Copyscape

About the Author

Vishvvas
Full Name: Vishwas Sutar
Member Level: HonoraryPlatinum
Member Status: Member,MVP
Member Since: 5/30/2011 2:13:10 AM
Country: India

http://www.dotnetfunda.com
Extensive and rich experience across gamut of technologies and programming languages like PB,VB,C++,VB.NET, C#, Classic ASP,ASP.NET, ASP.NET MVC.


About the Co-Author(s)

By Shweta Haridas Shweta Haridas Points: 40 | Level: Starter | Status: [Member]

Login to vote for this post.

Comments or Responses

Login to post response

Comment using Facebook(Author doesn't get notification)