![]() |
Development Lifecycle »
Design and Architecture »
Aspect Oriented Programming
Intermediate
License: The Code Project Open License (CPOL)
Implementing Logging & Transaction for NHibernate through Configuration files using Aspect Oriented Programming via UnityBy Niraj BhattApplying AOP using Unity on NHibernate |
C# 3.0.NET 3.5, SQL Server (SQL 2005, SQL 2008), Visual Studio (VS2008), ADO.NET, Dev
|
||||||||||
|
Advanced Search |
|
|
|
||||||||||||||||
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 this problem enhancing the overall readability & maintainability of the code while boosting team’s productivity.
I must admit it’s quite a big title to start with. Allow me to break the pieces. For 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 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 above code only two lines (Bold + Italic) do the actual work pertaining to business, 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.
To get started you need 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. 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 + "...");
//Call the next handler in the pipeline
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 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 code below uses TransactionScope (System.Transactions - ADO.NET 2.0) to provide an outer transaction. It ensures a commit on success of bussines logic & does rollback in case 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;
//Use logging in production
Console.WriteLine("Starting Transaction...");
using (TransactionScope scope = new TransactionScope())
{
returnMessage = getNext()(input, getNext);
//If Exception is thrown rollback
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 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); //Save a new Department to DB } public class DoWork : IDoWork { //Look ma, no cross cutting concerns... public void Work(string departmentName) { //N.B. NHibernateHelper is a helper class for Session //management; download the solution for its Source. //As an exercise you can try providing a //Session via Dependency Injection. 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?
The flow using Unity goes like this:
Implementation of above steps is shown below in code & config files:
class Program { static void Main() { //Step 1 var worker = Unity.UnityContainer.Resolve<IDoWork>(); worker.Work("Physics"); } }
//App.Config <unity> <containers> <container> <types> <!-- Step 2 --> <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"><!--Step 3--> <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"/><!--Step 4 --> </param> <param name="ignoreCase" parameterType="System.Boolean"> <value value="true" type="System.Boolean" /> </param> </constructor> </injection> </matchingRule> </matchingRules> <callHandlers><!--Step 5, 6 --> <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>
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 5 Jan 2009 Editor: Sean Ewington |
Copyright 2009 by Niraj Bhatt Everything else Copyright © CodeProject, 1999-2009 Web15 | Advertise on the Code Project |