Introduction
Cross Cutting concerns like Logging & Transaction are part of every enterprise application. Though required, sometimes they obscure written code taking more lines than the actual business logic. In addition, code reviewers / designers constantly struggle to ensure that developers don’t miss these important aspects in their piece of code. AOP with Unity gives a configurable solution to this problem enhancing the overall readability & maintainability of the code while boosting team’s productivity.
Background
I must admit it’s quite a big title to start with. Allow me to break the pieces. Those who are familiar with terms used in the title can skip this section.
Aspect Oriented Programming (AOP) is a technique which helps you focus on business logic keeping the cross cutting infrastructure code outside (for more information, you can refer to Wiki). For instance, Logging & transaction are two common cross cutting concerns. A typical enterprise application code would be as follows:
private static ILog log = LogManager.GetLogger(typeof(DepartmentFacade));
public IObservableList<Department> GetDepartments()
{
ISession session = null;
ITransaction trans = null;
try
{
log.Debug("Entering GetDepartments...");
session = NHibernateHelper.OpenSession();
trans = session.BeginTransaction();
DepartmentRepository departmentRepository = new DepartmentRepository(session);
var departments = departmentRepository.GetDepartments();
trans.Commit();
session.Close();
return departments;
}
catch (Exception ex)
{
log.Error("" + ex);
trans.Rollback();
session.Close();
throw;
}
finally
{
log.Debug("Exiting GetDepartments...");
}
}
In the above code, only two lines (Bold + Italic) do the actual work pertaining to business, the remaining lines are mostly addressing the cross cutting concerns. (N.B. Logging shown here is done using log4Net, you can refer to my blog entry for getting started with it).
Moving on to Unity, it’s a .NET based container from Microsoft Patterns & Practices team addressing developers needs like Dependency Injection (DI) & AOP. AOP is the main focus of this article.
NHibernate is a widely used open source O/R (Object – Relational) mapper. It addresses the impedance mismatch between Object and Relational worlds. Though I am going to show how to apply AOP over NHibernate, technique discussed here could be applied on any .NET code.
Building the Code
To get started, you need to download Unity Framework (1.2 is the latest release as I write this article) & compile the Unity VS.NET solution. Create a new Console Application (for simplicty sake) & Add References to all the Six generated assemblies as shown below:
The next step would be to create Unity CallHandlers (or interceptors), which would encapsulate an aspect of your code. You can create a CallHandler by Inheriting from Microsoft.Practices.Unity.InterceptionExtension
. ICallHandler
interface. The following code shows a LogHandler
:
public class LogHandler : ICallHandler
{
public int Order { get; set; }
public IMethodReturn Invoke
(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
ILog _log = LogManager.GetLogger(input.MethodBase.ReflectedType);
_log.Debug("Entering " + input.MethodBase.Name + "...");
var returnMessage = getNext()(input, getNext);
_log.Debug("Exiting " + input.MethodBase.Name + "...");
return returnMessage;
}
}
ICallHandler
interface contains a property called Order
which helps to prioritize multiple handlers. Invoke
method is the actual entry point for defining Handler
’s functionality. Invoke
method’s parameters types, IMethodInvocation
details about the method being invoked, while GetNextHandlerDelegate
refers to the next handler in the chain. For LogHandler
class, Invoke
method just logs the entry & exit of the method before / after invoking the next CallHandler
. Let’s create one more handler to make it slightly complex.
TranscationHandler
as shown in the code below uses TransactionScope
(System.Transactions
- ADO.NET 2.0) to provide an outer transaction. It ensures a commit on success of business logic & does rollback in case the business logic fails. A failure is normally indicated through an exception thrown by business logic. One needs to be careful for the elevation of a normal transaction to a distributed one, especially if your business logic is opening more than one NHibernate Sessions.
public class TransactionHandler : ICallHandler
{
public int Order { get; set; }
public IMethodReturn Invoke
(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
IMethodReturn returnMessage;
Console.WriteLine("Starting Transaction...");
using (TransactionScope scope = new TransactionScope())
{
returnMessage = getNext()(input, getNext);
if (returnMessage.Exception == null)
{
scope.Complete();
Console.WriteLine("Completeing Transaction...");
}
else
{
Console.WriteLine
("Aborting Transaction due to ..." + returnMessage.Exception);
}
}
return returnMessage;
}
}
Now we will define the interface and the implementation on which we will apply these aspects. AOP and DI require you to follow the design principle of programming to interface not to an implementation. So over here, we will define the interface IDoWork
& program against it (look for the main function definition later in the article). Implementation of the interface resides inside the DoWork
class (this will be injected by Unity as shown later), with important thing to note about, there is no explicit logging or transaction there.
public interface IDoWork
{
void Work(string departmentName);
}
public class DoWork : IDoWork
{
public void Work(string departmentName)
{
var session = NHibernateHelper.OpenSession();
var repository = new DepartmentRepository(session);
repository.SaveDepartment(new Department() { Name = departmentName });
session.Close();
}
}
So now comes the question, how do we apply our CallHandlers on the Work method, for making Logging & Transactions implicit?
Solution using Unity
The flow using Unity goes like this:
- You will ask Unity Container to give you the implementation of a specific interface type.
- Unity Container looks in the configuration file for implementation type mapped to your interface type.
- Before returning the implementation type instance back, Unity also looks at the policies defined in the configuration file.
- Every Policy has a matching rule, & matching rule in turn maps to a type.
- If Unity finds that there is a policy whose matching rule maps to the implementation type it’s about to return (step 2), then it performs actions outlined by policy before returning the implementation instance.
- This is where you can wrap the implementation instance with CallHandlers (in our case to make logging & transaction implicit).
Implementation of above steps is shown below in code & config files:
class Program
{
static void Main()
{
var worker = Unity.UnityContainer.Resolve<IDoWork>();
worker.Work("Physics");
}
}
//App.Config
<unity>
<containers>
<container>
<types>
<type type="UnityGettingStarted.IDoWork, UnityGettingStarted"
mapTo="UnityGettingStarted.DoWork, UnityGettingStarted" />
</types>
<extensions>
<add type="Microsoft.Practices.Unity.InterceptionExtension.Interception,
Microsoft.Practices.Unity.Interception" />
</extensions>
<extensionConfig>
<add name="interception"
type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.
InterceptionConfigurationElement,
Microsoft.Practices.Unity.Interception.Configuration">
<policies>
<policy name="InterceptingDoWorkPolicy">
<matchingRules>
<matchingRule name="MatchDoWork"
type="Microsoft.Practices.Unity.InterceptionExtension.TypeMatchingRule,
Microsoft.Practices.Unity.Interception">
<injection>
<constructor>
<param name="typeName" parameterType="System.String">
<value value="UnityGettingStarted.DoWork"
type="System.String"/>
</param>
<param name="ignoreCase" parameterType="System.Boolean">
<value value="true" type="System.Boolean" />
</param>
</constructor>
</injection>
</matchingRule>
</matchingRules>
<callHandlers>
<callHandler name="LogHandler"
type="UnityGettingStarted.LogHandler, UnityGettingStarted" />
<callHandler name="TransactionHandler"
type="UnityGettingStarted.TransactionHandler, UnityGettingStarted" />
</callHandlers>
</policy>
</policies>
<interceptors>
<interceptor
type="Microsoft.Practices.Unity.InterceptionExtension.InterfaceInterceptor,
Microsoft.Practices.Unity.Interception">
<default type="UnityGettingStarted.IDoWork, UnityGettingStarted" />
</interceptor>
</interceptors>
</add>
</extensionConfig>
</container>
</containers>
</unity>
Conclusion
This article shows how to implement common cross cutting concerns like transaction & logging using AOP with Unity. AOP with Unity is a powerful technique & once mastered can create substantial value for the entire team.
History
- 5th January, 2009: Initial post