Aspect Oriented Programming (AOP) aims to increase modularity by separation of cross-cutting concerns. Without knowing about the jargon and software engineering terms, probably the first time a software developer thinks about this is when s/he is thinking about implementing logging and exception handling in an application.
If you are an old VB6 developer who used to write the whole application in one module (I used to do that), you are going to implement something like this:
var client = new CalculatorService();
this.logger.write(string.Format("Adding Request: {0} + {1}", a, b));
var result = client.Add(1, 2);
this.logger.write(string.Format("Adding Response: {0}", result));
Every time your application wants to call another web service you need to call logger before and after the invocation – which is fine. However, when you are calling lots of operations, you may need to revaluate your hypothesis.
You need a mechanism to say whenever a method with certain conditions is being called, log the input/output before and after its invocation. One way to do that is using interception pattern. There are lots of libraries to implement such thing in .net but my favourite ones are Sprint.Net, Castle, and Unity. If you are a Java developer probably you prefer Spring.net since it is quiet similar to Spring experience you had in Java Spring. Castle Windsor is somehow a de facto standard for dependency injection and interception in .net world. Lots of Mocking frameworks such as Moq and Ninject are using it for their implementing. I am using Unity in this example since it is a standard Microsoft solution shipping as a part of Enterprise Library (but not necessary the best solution for all).
Install Enterprise Library so that you have all the required assemblies. I am using Enterprise Library 5.0 and Unity 2.0.414. The configurations for Unity 2.1 is different, so you need to update them if you are using Unity 2.1+.
public interface ICalculator
{
double CalculateAdd(double a, double b);
double CalculateMultiply(double a, double b);
double CalculateMinus(double a, double b);
double CalculateDevide(double a, double b);
}
public class Calculator : ICalculator
{
public double CalculateAdd(double a, double b) { return a + b; }
public double CalculateMultiply(double a, double b) { return a * b; }
public double CalculateMinus(double a, double b) { return a - b; }
public double CalculateDivide(double a, double b) { return a / b; }
}
The Calculator class is implementing ICalculator and it is being called by our simple console application. we need all the methods intercepted before and after the invocation and log the input and output using Logger class as below:
public interface ILogger
{
void Log(string value);
}
public class Logger : ILogger
{
public void Log(string value)
{
Console.WriteLine(value);
}
}
We need a class to implement the interception. This class should implement IInterceptionBehavior.
namespace Infrastructure
{
public class LoggingInterceptionBehavior : IInterceptionBehavior
{
private readonly ILogger logger;
public IEnumerable GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public bool WillExecute
{
get { return true; }
}
public LoggingInterceptionBehavior(ILogger logger)
{
this.logger = logger;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
var traceId = System.Guid.NewGuid().ToString();
this.logger.Log(string.Format("{0} - Before Invoking {1}"), traceId, input.MethodBase.Name));
var result = getNext()(input, getNext);
if (result.Exception != null)
{
this.logger.Log(string.Format("{0} - After Invoking Exception {1} - {2}"), traceId, input.MethodBase.Name, result.Exception.Message));
}
else
{
this.logger.Log(string.Format("{0} - After Invoking Exception {1} - {2}"), traceId, input.MethodBase.Name, result.ReturnValue));
}
return result;
}
}
}
In this example we just need to change the LoggingInterceptionBehavior method. Every time a method that is going to be intercepted is invoked, instead of triggering the method, Unity calls the Invoke method in the above sample. Input parameter (IMethodInvocation) contains all the information about the method such as it’s name, it’s parameters. Any value that is provided in future such as method’s outputs or exceptions after its invocation will be saved in this variable. there is a parameter of type GetNextInterceptionBehaviorDelegate that is a delegate holding a pointer to the actual method should be called. In the implementation of Invoke method in line 29 (var result = getNext()(input, getNext);) is the crisis point. We can implements everything we want to be implemented for interception before and after that line.
Now we can configure unity. In this example I used configuration file rather than design time method.
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=2.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<!-- Using Interception -->
<container name="Interception">
<extension type="Interception" />
<register type="AopTest.ILogger, AopTest" mapTo="AopTest.Logger, AopTest" />
<register type="AopTest.ICalculator, AopTest" mapTo="AopTest.Calculator, AopTest" />
<interceptor type="InterfaceInterceptor" />
<interceptionBehavior type="Infrastructure.LoggingInterceptionBehavior, Infrastructure" />
</register>
</container>
</configuration>
The key points are as below:
- For interception functionality, we load an extension and use that to extend Unity.
- We map ILogger to Logger and ICalculator to Calculator for the sake of Dependency Injection.
- When registering ICalculator, we explicitly mention that this class should be intercepted and to intercept this we want to use Infrastructure.LoggingInterceptionBehavior we created in previous step.
- I saved the above file outside my main application configuration file (in unity.config) since it may get very big over time.
To start the application we need to:
- Load configuration file and resolve the instance of ICalculator.
- Call the ICalculator instance.
- Any method in ICalculator should be intercepted now.
- The dependency of Infrastructure.LoggingInterceptionBehavior to ILogger is resolved in runtime automatically by unity since it is configured in the config file.
// Configuring unity container
var fileMap = new System.Configuration.ExeConfigurationFileMap
{
ExeConfigFilename = "unity.config"
};
var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
var unitySection = (UnityConfigurationSection)configuration.GetSection("unity");
var uc = new UnityContainer().LoadConfiguration(unitySection, "Interception");
// Resolving instance
var calculator = uc.Resolve();
// Calling methods
var addResult = calculator.CalculateAdd(1, 2);
var multiplyResult = calculator.CalculateMultiply(1, 2);
If you put a breakpoint on like 18 and step into, you notice that instead of calling CalculateAdd directly, it calls the Invoke method while getNext parameter is a delegate of CalculateAdd.
In the next post, I show a more granular method of implementing interception using policy injection.
Image Credit: MemeCrunch