Click here to Skip to main content
Click here to Skip to main content

Implementing Logging & Transaction for NHibernate through Configuration Files using Aspect Oriented Programming via Unity

, 5 Jan 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Applying AOP using Unity on NHibernate

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:

AOPWithUnity

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 + "...");
            //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 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;
            //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 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); //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?

Solution using Unity

The flow using Unity goes like this:

  1. You will ask Unity Container to give you the implementation of a specific interface type.
  2. Unity Container looks in the configuration file for implementation type mapped to your interface type.
  3. Before returning the implementation type instance back, Unity also looks at the policies defined in the configuration file.
  4. Every Policy has a matching rule, & matching rule in turn maps to a type.
  5. 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.
  6. 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()
        {            
            //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>

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author


Comments and Discussions

 
GeneralMy vote of 5 PinmvpKanasz Robert28-Sep-12 8:14 
QuestionAnother class PinmemberDerek Xiao13-Jan-09 21:44 
Hey,Niraj
Very good and simple article for learning Unity, i have some confuse, just if there is another interface name IOtherWork, and there is also an implementation class OtherWork. This class maybe has a Update() method, can i still use the LogHandler and TransactionHandler?
 
If can, i add next code
var otherWorker = Unity.UnityContainer.Resolve();
otherWorker.Update(1, "new value");
in the main() after worker.Work(), what is the sequence of TransactionHandler process?
 
it looks disheveled, could you clear what i say? thanks.

AnswerRe: Another class PinmemberNiraj Bhatt14-Jan-09 7:28 
GeneralSeveral methods in transaction PinmemberJorgeVinagre7-Jan-09 5:48 
GeneralRe: Several methods in transaction PinmemberNiraj Bhatt7-Jan-09 7:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411023.1 | Last Updated 5 Jan 2009
Article Copyright 2009 by Niraj Bhatt
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid