Tag Archives: UnitOfWork

Code

UnitOfWork Attribute for WebAPI

In one of my previous post I mentioned the UnitOfWork concept and created an attribute for asp.net MVC which creates a transaction and wraps an entire request inside.  The MVC attribute closed it’s transaction after the view has rendered, this runs on the OnResultExecuted method.

In WebApi how ever there is no method that is fired after the response has been serialised. This may not seem like a problem; however, if you are returning a collection as IEnumerable<> from an ApiController or anything that lazily executes from you then then unit of work will closes it’s transaction BEFORE you’ve pulled back your data. So far the only way I’ve found to get around this problem is instead to return an IList<> which forces the execution.

Below is a the UniOfWork attribute I’ve been using for WebApi, it also rolls back the transaction if an exception occurs as oppose to the MVC way.

 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
  public sealed class UnitOfWorkFilter : ActionFilterAttribute, IExceptionFilter
  {
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
      var unitOfWork = (IUnitOfWork)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnitOfWork));

      unitOfWork.EndTransaction();
    }

    public override void OnActionExecuting(HttpActionContext actionExecutingContext)
    {
      var unitOfWork = (IUnitOfWork)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnitOfWork));
      unitOfWork.BeginTransaction();
    }

    public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
    {
      var unitOfWork = (IUnitOfWork)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnitOfWork));
      unitOfWork.RollBack();
      return Task.Run(() => { }, cancellationToken);
    }
  }
Asp.Net MVC Code

Unit of Work, NHibnernate and Asp.Net MVC

In a previous post I mentioned for some projects I’m using a combination of NHibernate, unit of work and asp.net mvc; this post will give an example of how I set this up for a project and how to get mvc to automatically create instances of UoW, wrap our code in transactions and clean up after it’s self via ActionFilterAttributes. Some of the code I have is a modified version of this but probably not as nice.

Here is a list of the interfaces that’ll be created:

And classes that implement them:

And finally the mvc Attribute: UnitOfWorkFilter.

This maybe(is) a lot of work for for something that seems pretty straight forward but it allows the code to be decoupled and makes testing much easier.  For example a project I’ve been working on uses this workflow and has a windows service to do some back end processing; by creating a RequestState to work with a windows service the rest of the code doesn’t need changing (apart from the filter obviously.)

I’ll leave the code further down in the post so things don’t get messy.

Actually using this setup.

This post makes the assumption that your data access is all via nhibernate and you’re using the repository pattern or something similar. Your repositories will need access to the IActiveSessionManager. If you’re using a DI like Ninject it should pass it through for you.

In your repository you can access the nhibernate session like so:

this.activeSessionManager.GetActiveSession();

I personally have a property to do this for me:

    public ISession Session
    {
      get
      {
        return this.activeSessionManager.GetActiveSession();
      }
    }

From here on do what ever queries you need, all the repositories in the will share the same session during the same request.

Applying the attribute

You can either have the UnitOfWorkFilter attached to each controller/method or in the global.asax you can register is as a global filter.

 [UnitOfWorkFilter]
  public class UserController : Controller
{
}

or

    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
      filters.Add(new UnitOfWorkFilter());

    }

That’s it!.

How it all fits together

Imagine you have a controller that gets you a list of users (UserController).

  1. You issue a GET to the controller action
  2. The UnitOfWorkFilter kicks in and starts a new transaction from the ActiveSessionManager (The same manager your repo’s use)
  3. The ActiveSessionManager checks for/creates a new nhibernate session to work with
  4. Your repos use this ActiveSessionManager/NHibernate session to get its data
    1. If you have more than one repo in the controller action then no new sessions are created, the same one is shared.
  5. The controller returns the users
  6. The UnitOfWorkFilter then commits the transaction and closes the current session
    1. If something goes wrong like an exception the the UnitOfWorkFilter rollsback the transaction – preserving the integrity of the database

Below is the code to implement this.

IUnitOfWork

  public interface IUnitOfWork : IDisposable
  {

    void BeginTransaction();

    void EndTransaction();

    void RollBack();
  }

IRequestState

  public interface IRequestState
  {
    T Get<T>(string key);

    void Store(string key, object something);
  }

ISessionProvider

The ISession is the NHibernate session.

  public interface ISessionProvider
  {
    ISession Create();
  }

IActiveSessionManager

  public interface IActiveSessionManager: IDisposable
  {
    ISession GetActiveSession();

    void ClearActiveSession();

    bool HasActiveSession { get; }
  }

UnitOfWork

/// <summary>
  /// An nhibernate specific implementation of the unit of work concept
  /// </summary>
  public class UnitOfWork : IUnitOfWork
  {
    private readonly IActiveSessionManager sessionManager;

    private ITransaction transactionScope;

    public UnitOfWork(IActiveSessionManager sessionManager)
    {
      this.sessionManager = sessionManager;
    }

    public void Dispose()
    {
      if (this.transactionScope != null)
      {
        this.transactionScope.Dispose();
      }

      this.sessionManager.Dispose();
    }

    public void BeginTransaction()
    {
      if (this.transactionScope != null)
      {
        throw new InvalidOperationException("Transaction already started; nested transactions are not supported");
      }

      this.transactionScope = this.sessionManager.GetActiveSession().BeginTransaction();
    }

    public void EndTransaction()
    {
      if (this.transactionScope == null)
      {
        throw new InvalidOperationException("No active transaction found");
      }

      if (!this.transactionScope.IsActive)
      {
        throw new InvalidOperationException("This transaction is no longer active");
      }

      try
      {
        this.transactionScope.Commit();
      }
      catch (Exception ex1)
      {
          this.RollBack();
      }
    }

    public void RollBack()
    {
      this.transactionScope.Rollback();
    }
  }

AspNetRequestState

  public class AspNetRequestState : IRequestState
  {
    public T Get<T>(string key)
    {
      return (T)HttpContext.Current.Items[key];
    }

    public void Store(string key, object something)
    {
      HttpContext.Current.Items[key] = something;
    }
  }

SessionProvider

Note that the session factory has not been configured, this is left up you to configure.

 public class SessionProvider : ISessionProvider
  {
    private static ISessionFactory sessionFactory;

    public SessionProvider()
    {
      if (sessionFactory == null)
      {
	    // Do your mhibernate configuration here
        var configration = new Configuration();
        sessionFactory = configration.BuildSessionFactory();
      }
    }

    public ISession Create()
    {
      return sessionFactory.OpenSession();
    }
  }

ActiveSessionManager

public class ActiveSessionManager : IActiveSessionManager
  {
    private const string sessionKey = "_currentSession";

    private readonly IRequestState requestState;

    private readonly ISessionProvider sessionProvider;

    public ActiveSessionManager(IRequestState requestState, ISessionProvider sessionProvider)
    {
      this.requestState = requestState;
      this.sessionProvider = sessionProvider;
    }

    public ISession GetActiveSession()
    {
      if (this.Current == null)
      {
        var newSession= this.sessionProvider.Create();
        this.Current = newSession;
        return newSession;
      }

      return this.Current;
    }

    public void ClearActiveSession()
    {
      this.Current = null;
    }

    public bool HasActiveSession
    {
      get { return this.Current != null; }
    }

    protected virtual ISession Current
    {
      get
      {
        return requestState.Get<ISession>(sessionKey);
      }
      set
      {
        requestState.Store(sessionKey, value);
      }
    }

    public void Dispose()
    {
      var session = this.Current;
      if(session != null)
      {
        session.Dispose();
        this.ClearActiveSession();
      }
    }
  }

 

UnitOfWorkFilter

 

public class UnitOfWorkFilter : ActionFilterAttribute
  {
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
      var unitOfWork = DependencyResolver.Current.GetService<IUnitOfWork>();
      unitOfWork.BeginTransaction();
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
      var unitOfWork = DependencyResolver.Current.GetService<IUnitOfWork>();
      unitOfWork.EndTransaction();
    }
  }
Asp.Net MVC Code

NHibernate leaking connections with UnitOfWork pattern and transactions

Recently I’ve been using NHibernate in a project at work along with the unit of work pattern in an asp.net mvc site. Only 1 instance of the UoW is created per request and disposed of at end of the request.

In fact here is a UoW class we’re using:

public class UnitOfWork : INHibernateUnitOfWork
  {
    /// <summary>
    /// The session factory
    /// </summary>
    private readonly ISessionFactory sessionFactory;

    /// <summary>
    /// The current transaction
    /// </summary>
    private readonly ITransaction transaction;

    /// <summary>
    /// Gets the session.
    /// </summary>
    /// <value>The session.</value>
    public ISession Session { get; private set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="UnitOfWork"/> class.
    /// </summary>
    /// <param name="sessionFactory">The session factory.</param>
    public UnitOfWork(ISessionFactory sessionFactory)
    {
      this.sessionFactory = sessionFactory;

      Session = this.sessionFactory.OpenSession();
      Session.FlushMode = FlushMode.Auto;
      transaction = Session.BeginTransaction();
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
      transaction.Dispose();
    }

    /// <summary>
    /// Commits this instance.
    /// </summary>
    public void Commit()
    {
      if (!transaction.IsActive)
      {
        throw new InvalidOperationException("No active transation");
      }

     /transaction.Commit();
    }

    /// <summary>
    /// Rollbacks this instance.
    /// </summary>
    public void Rollback()
    {
      if (transaction.IsActive)
      {
        transaction.Rollback();
      }
    }
  }

As you can see in the constructor we create a new NHibernate transaction and dispose of it when the request ends (by calling the dispose method).

The problem with this is that it created a connection with an open transaction but never closed it. Any way to resolve this we removed the use of transactions (until such a time they are working or we’ve figured out a suitable alternative). Two changes where made to the dispose and commit method to clean up the session instance and make sure the data is flushed.

    public void Dispose()
    {
      Session.Dispose();
    }

    public void Commit()
    {
      Session.Flush();
    }

Note: This project also is using the Velocity cache provider and by removing the transactions / replacing the code the caching mechanisms still work.