Unit of work

You might argue that we do not need unit of work in the previous situation as we are only saving a single entity using one call to ISession.Save. But remember that saving an employee instance results in multiple database records being inserted, and ideally, the whole operation of inserting these multiple records in database should be an atomic operation. Unit of work guarantees the atomicity we need. Before we bring a notion of unit of work, I would like to spend some time talking about scope of unit of work in this particular situation. A unit of work has to begin somewhere and end somewhere. Determining where unit of work begins and ends is crucial to robustness of data access layer.

Scope of unit of work for web applications

Web application design is an interesting topic. This book is not on web application design hence I will not spend too much time on it. But there is one aspect of web application design that is critical in deciding the scope of unit of work. Is your web application designed in such a way that every request performs a task that in itself can qualify as single business operation? Or in other words, does your web application handle any request that constitutes multiple business operations that can be finished independently of each other? If you do, then you are probably doing it wrong or you are making it difficult on yourself. Consider the following scenario.

You have a web application which takes some input from end user and then works on that input. Application performs two tasks, namely A and B, with those inputs. This is not clear to the user from the UI that they see but that is what happens behind the scenes. If both tasks A and B finish, then you take your users to a nice looking success page. But what if task A finishes but task B fails? Since your users do not know that there is task A and task B, you cannot tell them, "sorry folks, task B failed and you would need to provide the input again". Let's say you could do that; then what about task A that finished? If task A updated some database records (or inserted new ones), are you going to revert those operations? If you do not revert the operations, how would you ensure that next time the user provides the same input, you only perform task B because task A was finished already? Are you going to design your application as a state machine so that you remember where users left off last time?

You can see that there are too many questions and too many things to think about. I do not need to tell you that such a design would need lot of code to support it and even after that there would still be edge cases that would make your application fail over. So a decent and reliable design that you can implement with least amount of code is the one that wraps a single business operation in a web request. Such a design is also better from error recovery point of view. Now if you are going to code a single business operation per request, then we can safely say that if nothing goes wrong with processing of that request then the business operation can be called to have finished successfully. If there is any error during processing of the request then business operation fails. Any database changes made during a successful business operation should be committed at the end of the operation. The scope of unit of work needs to span across the whole business operation. In this design, a request wraps one business operation so we can say that scope of unit of work can span the whole request.

Session per request

We know that ISession can act as unit of work. We have also established that a good design of web application extends the scope of unit of work to full request processing. So an instance of ISession for every incoming request, with its inherent unit of work capability, seems to fit nicely here. You open a new session at a point where incoming request first hits your code. At the same time, you begin a transaction. You use that session whenever you need to interact with database during processing of that request. The last point in your code before request leaves is where you decide whether you should commit the transaction and close the session or rollback the transaction and then close the session. Sounds like it can work. Rather, this is a pattern so famous that it is recognized by its own name "session per request" and NHibernate supports it natively through an implementation of contextual sessions which is what we learn next.

Contextual sessions

In order to implement "session per request" kind of pattern, we need an ability to hold on to a session as long as the request is being processed. In other words, the scope of session needs to be limited by lifetime of the request in whose context the session was created. Such sessions can be called contextual sessions. There are two mechanisms that let us have contextual sessions. One of the mechanisms is natively supported by NHibernate whereas the other mechanism is offered by most famous IoC containers. We will look at the native NHibernate offering in detail and leave the IoC container mechanism for our avid readers to explore on their own.

NHibernate's implementation of contextual sessions hinges around the following three operations:

  • Seamlessly store a session object
  • Seamlessly retrieve previously stored session object
  • Mark beginning and end of a context in which session object needs to be used

The way this works is – when a new request comes in, you create a new session by calling the OpenSession method on session factory. You then tell NHibernate that a new context starts here and NHibernate stores the session object created. NHibernate has defined a class named NHibernate.Context.CurrentSessionContext. This class provides a method Bind which is used to signal NHibernate of beginning of context. From this point onwards, if you need this session object then you can call the GetCurrentSession method on session factory. After the request is processed, your context ends and you can call the Unbind method on CurrentSessionContext to tell NHibernate that the context has ended and it no longer needs to hold on to the session object.

It is possible that the previous text has left you even more confused and hence we will look at a code listing which should explain this in a better way. Following code listing shows implementation of a class SessionManager which we can use whenever we need access to current session object:

using NHibernate;
using NHibernate.Context;

namespace Persistence
{
  public class SessionManager
  {
    private static readonly ISessionFactory sessionFactory;

    static SessionManager()
    {
      sessionFactory = new DatabaseConfiguration()
                      .BuildSessionFactory();
    }

    public static ISession GetCurrentSession()
    {
      if (CurrentSessionContext.HasBind(sessionFactory))
      {
        return sessionFactory.GetCurrentSession();
      }

      var session = sessionFactory.OpenSession();
      CurrentSessionContext.Bind(session);
      return session;
    }

    public static void Unbind()
    {
      CurrentSessionContext.Unbind(sessionFactory);
    }
  }
}

First and the most important thing about the preceding code is that the GetCurrentSession method is made static so that it can be called from anywhere. There is no need to instantiate the SessionManager class in order to get instance of session. We would look at code inside the GetCurrentSession method line by line. Before that, note that there is a static constructor where session factory is built.

The first line in GetCurrentSession is an if condition which calls CurrentSessionContext.HasBind and passes the session factory to it. We are seeing this method for the first time. This method returns true if an existing session has been tracked by the context already. In that case, we just call sessionFactory.GetCurrentSession() to return that tracked session. If no session is tracked, then we open a new session by calling sessionFactory.OpenSession(). The next line passes this newly created session to the CurrentSessionContext.Bind method to tell the session context that it should track this session.

So first call to SessionManager.GetCurrentSession will open a new session, add it to context, and return it. Subsequent calls to this method would return the same session object.

When processing of the request finishes, you can tell NHibernate to forget about the session object by calling the SessionManager.Unbind method. This method internally calls the CurrentSessionContext.Unbind method which takes session factory as input.

This looks good so far. But the following two questions are still unanswered:

  • When SessionManager.GetCurrentSession is called from multiple threads for multiple requests, do we get a new session object or the same session object? How does CurrentSessionContext ensure that every web request gets its own session object?
  • We kept referring to beginning of request processing and end of request processing in the above discussion. Those are the two points from where the earlier code would be called. But where is beginning of request and end of request for an ASP.NET MVC application, for example?

Answer to the first question lies in the implementation of CurrentSessionContext. There are more than one implementations of CurrentSessionContext. You can configure which one you want to use for you application. We would discuss this next. Second question is more of an ASP.NET MVC detail and has nothing to do with NHibernate. After we discuss the configuration of CurrentSessionContext, we would turn our attention to this aspect.

Configuring CurrentSessionContext

Implementation of CurrentSessionContext is pluggable. What that means is that you can inherit from this class and have your own implementation of contextual sessions. You can then choose to have storage of contextual sessions managed in a better and thread-safe way so that no two web requests get hold of the same session object. The good news is that NHibernate provides an implementation which does exactly that. NHibernate implements a class WebSessionContext which uses HttpContext to store the session. This stored instance of session is available to use throughout the processing of the request. During NHibernate configuration you can declare that you want to use WebSessionContext as your current session context class. Following code shows how to do this:

public class DatabaseConfiguration : Configuration
{
  public DatabaseConfiguration()
  {
    this.CurrentSessionContext<WebSessionContext>();
  }
}

Note that there is other NHibernate configuration in the preceding class which is not shown here in order to save space. If you remember from Chapter 4, NHibernate Warmup, programmatic configuration of NHibernate is done using the NHibernate.Cfg.Configuration class. You can instantiate this class directly. Here I have preferred to inherit from that class and declared the entire configuration inside the inherited class. The Configuration class exposes a method CurrentSessionContext<T>. This method is used to tell NHibernate which implementation of contextual session we want to use. Since our application is a web application, we have used WebSessionContext here but the following different implementations are available to use in different situations. Remember that all these implementations do the exact same thing – track session by storing them somewhere. Where they differ is the storage mechanism that is used which makes them suitable to be used in different kind of applications/setups.

  • CallSessionContext: This uses CallContext to track current session. CallContext is a specialized collection class available in .NET which provides set of properties and data slots that are carried along with execution code path. These data slots are unique to each logical thread. When the execution path involves a remote call, CallContext is transparently copied over to the remote side.
  • ThreadStaticSessionContext: This implementation uses a static variable local to current thread for tracking current session. It uses the ThreadStatic attribute of .NET in doing so. This implementation is most useful in desktop applications such as WinForms or other multi-threaded but non-web applications.
  • WcfOperationSessionContext: If you are working with WCF services then you can use this implementation to track your current session. This implementation uses OperationContext offered by WCF to store the current session instance.

If the previous OOTB implementations do not help with your situation then you can always implement your own. But I am yet to come across a situation where this is needed. Now let's look into implementing session per request using contextual sessions.

Implementing session per request using the contextual sessions

To implement "session per request" pattern, you need to be able to create a new session when a new request comes in, then use that session throughout the request processing and in the end, dispose the session. .NET provides hooks that you can use to intercept requests when they come in and leave. All web/service frameworks of .NET provide such hooks. The idea is to use these hooks to create a new session when a new request comes in and dispose the session when the request processing is done. Let's quickly run through different options available for hooking into request pipeline to implement "session per request".

Using the HTTP module

HTTP modules are one of the oldest mechanisms of building pluggable components that are capable of intercepting incoming and outgoing requests. An HTTP module is a class that implements the IHttpModule interface. An HTTP module has a Init method to which an instance of HttpApplication is passed. HttpApplication exposes events to which we can subscribe. In this case, we can subscribe to BeginRequest, which is invoked at the beginning of every incoming request, and EndRequest, which is invoked at the end of processing of every request. Following code listing shows the simplest implementation of such an HTTP module:

public class DatabaseSessionModule : IHttpModule
{
  public void Init(HttpApplication context)
  {
    context.BeginRequest += Context_BeginRequest;
    context.EndRequest += Context_EndRequest;
  }

  private void Context_EndRequest(object sender, System.EventArgs e)
  {
var session = SessionManager.GetCurrentSession();
session.Close();
session.Dispose();
  }

  private void Context_BeginRequest(object sender, System.EventArgs e)
  {
var session = SessionManager.GetCurrentSession();
  }

  public void Dispose()
  {

  }
}

Configuring an HTTP module is outside the scope of this book so I will jump straight into the part that is of interest to us. We have added two methods to subscribe to the BeginRequest and EndRequest events. In method Context_BeingRequest, we create a new session object which would be tracked by NHibernate's contextual session implementation and is available throughout the request processing. In Context_EndRequest, we would close and dispose that session.

Using hooks in the global.ascx file

If you have developed web applications using .NET then you know that there is a global.ascx file in every web application. This file contains the entry point of your application. The code-behind file for this also has options to set up some hooks that are called by frameworks on some events. This is very similar to HTTP module. Like HTTP modules, two events that are of interest to us are Application_BeginRequest and Application_EndRequest. Application_BeginRequest is called at the beginning of every incoming request and Application_EndRequest is called in the end, after every request is processed. Rest of the logic is exactly same as for HTTP module. Following code listing shows the relevant part of global.ascs.cs:

public class MvcApplication : HttpApplication
{
  protected void Application_Start()
  {
    //Other MVC specific code
  }

  protected void Application_BeginRequest(object sender, EventArgs e)
  {
    var session = SessionManager.GetCurrentSession();
  }

  protected void Application_EndRequest(object sender, EventArgs e)
  {
    var session = SessionManager.GetCurrentSession();
    session.Close();
    session.Dispose();
  }
}

Using the action filters of ASP.NET MVC or Web API

This is the newest and most configurable option of all. Action filters, like HTTP modules, intercept incoming and outgoing requests. Unlike HTTP modules, the scope of action filters is limited to MVC or Web API part of the application. Besides that, action filters provide nice control over which of the incoming requests should be intercepted by a particular action filter. You can also have global action filters which apply to all requests. Following code listing shows how to implement "session per request" using action filter:

public class RequiresDatabaseSession : ActionFilterAttribute
{
  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    var session = SessionManager.GetCurrentSession();
  }

  public override void OnActionExecuted(ActionExecutedContext filterContext)
  {
    var session = SessionManager.GetCurrentSession();
    session.Close();
    session.Dispose();
  }
}

This is just like any other MVC action filter. If you have used action filters before then you know that action filters provide various event handlers which are called by MVC framework at appropriate times. Two of such event handlers are OnActoinExecuting and OnActionExecuted. Former is called before the code from controller action is executed and the latter is called after the controller action is executed. We have acquired an instance of session during OnActionExecuting and released it during OnActionExecuted.

WCF

WCF offers hooks in the form of pluggable message inspectors. This works similar to MVC action filters in that you can specify which WCF endpoints you want to apply a particular message inspector to. As the name suggests, a message inspector is capable of intercepting and inspecting incoming and outgoing messages. In this case, we would need to implement a specific message inspector called dispatch message inspector. Dispatch message inspector needs to implement the following interface:

public interface IDispatchMessageInspector
{
  public object AfterReceiveRequest(
  ref System.ServiceModel.Channels.Message request,
  IClientChannel channel, InstanceContext instanceContext;

public void BeforeSendReply(
  ref System.ServiceModel.Channels.Message reply,
  object correlationState;

}

Barring the details of the parameters that are going into the methods, this is a very simple interface. You have got two methods on this interface. AfterReceiveRequest is called when service receives a client request for processing. At this point we would open a new session. BeforeSendReply is called after we are done servicing the request and a response is about to be sent to the client. It is in this method that we would close and dispose the session.

I do not intend to go into the details of full implementation of a dispatch message inspector for WCF. This is left as a self-exercise for readers.

Note

In every implementation, we are opening a new session as soon as a request is received. This is in line with "session per request" but it is not really needed. You can defer opening of a new session till a time when session is first needed during request processing.

Next, we would extend the previous examples to add unit of work implementation.

Unit of work implementation

We have talked about unit of work in quite some detail so far. You probably have a very clear picture of what unit of work does. In this section, we would quickly look at couple of different ways of implementing unit of work.

Unit of work spanning the whole HTTP request

In earlier section when we talked about unit of work, we also discussed how ISession inherently offers unit of work when all interaction with ISession is done inside a transaction. All we needed to do was place a call to BeginTransaction after a session is opened. The same concept can be combined with "session per request" implementation to provide a complete unit of work pattern making most of NHibernate's capabilities. Let's take a look at the following code listing:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
  var session = SessionManager.GetCurrentSession();
  session.BeginTransaction();
}

This is exactly the same code that we saw earlier. We have only added an extra line of code to open a new transaction every time a new session is created. We are effectively adding any database interaction done using this session to a transaction, thus making that interaction a part of unit of work.

The BeginTransaction statement sends a SQL command to database to being a SQL transaction. This command is sent to database immediately. So you have an open database transaction as long as your request is being processed. If your request takes a long time to process, then transaction remains open for a long time. This may have negative impact on other SQL queries that are waiting to be executed. I tend to see this as a positive thing because it really forces me to ensure that 1) a single request is not taking long time to execute and 2) my requests (and overall application) are designed so that we do not have less requests doing bulky jobs but more requests doing small, meaningful chunks of work. But if you are worried about this, you can always move the call to BeginTransaction inside SessionManager and remove the call to GetCurrentSession from OnActionExecuting. If you do that, make sure that you call BeginTransaction only when a new session is being created.

The transaction that we started needs to be committed at some point (or everything rolled back if anything goes wrong). Let's take a look at modified code of OnActionExecuted which achieves this:

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
  var session = SessionManager.GetCurrentSession();
  var transaction = session.Transaction;

  if (transaction.IsActive)
  {
    if (filterContext.Exception != null)
    {
      transaction.Rollback();
    }
    else
    {
      transaction.Commit();
    }
  }
  session.Close();
  session.Dispose();
}

There are two interesting things to note here. First, we get hold of the transaction that is attached to the current session (this is same transaction that we started in OnActionExecuting), and if there is any exception thrown during the execution of controller action then we rollback the transaction. Second, if there was no error during execution of action then we commit the transaction. We close and dispose the session before leaving the method.

Note

This same implementation can be extended to other ways of implementing contextual session that we saw using the HTTP module or hooks in global.asax.

Note that the scope of an action filter is limited to controller action only. If you have got some logic in a different action filter or in an HTTP module, then any database interaction from that place would not be part of the unit of work that is initiated from this action filter. You are effectively not doing "session per request" in that situation. There is nothing wrong in doing so as long as you know that there are multiple units of work at play and one or more of units of work may be committed to database even if a latter unit of work fails because of some error. Make sure that such a situation does not leave your database in an inconsistent state.

Unit of work with custom scope

Sometimes we may not want to put all database interactions within a request in a single unit of work. For those times, the above implementation does not work and we need to implement a custom unit of work which can be invoked from anywhere. I have been using the following simple implementation with some success:

public class UnitOfWork
{
  private readonly ITransaction transaction;

  public UnitOfWork(ISession session)
  {
    transaction = session.BeginTransaction();
  }

  public void Commit()
  {
    if(transaction.IsActive) transaction.Commit();
  }

  public void Rollback()
  {
    if(transaction.IsActive) transaction.Rollback();
  }
}

Whenever you need to add database activities in a unit of work, you just instantiate a new instance of the UnitOfWork class and pass the session object that you are using. You can then call either Commit or Rollback depending on whether you want to commit your changes or discard them.

This is a very simple implementation that should give you an idea of what can be done. I have skipped some basic validations such as checking that session is not null, making sure that transaction has not started already on the session object, and so on. Feel free to add that logic if you happen to use unit of work with custom scope. Another thing you could do is to make the above class disposable by implementing the IDisposable interface. You can then call the Rollback method from the Dispose method of IDisposable and thus forever end the need to call Rolllback manually. Personally, I use such an implementation very sparingly.

That concludes our long discussion on managing session and unit of work. We discussed multiple concepts and implementations which may be confusing. Important take-away from this discussion is to understand the role that session and unit of work play when it comes to integrity of critical business data. Let's continue our journey of implementing other features.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset