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.
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 methodusing 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 interceptionInterface:
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