TransactionController

Transactions are a common cross-cutting concern, that need to be managed carefully.

You often want a transaction to be shared between multiple repositories, but you don’t want low level details (e.g. what ORM you are using) to bleed out of the persistence layer.

There’s also a lot of boiler plate code, and forgetting to commit the transaction is a common (and easy) mistake to make.

Bring the Func!

	public class TransactionController : ITransactionController
	{
		private readonly ISession _session;

		public TransactionController(ISession session)
		{
			_session = session;
		}

		public void InTransaction(Action action)
		{
			using (var transaction = _session.BeginTransaction())
			{
				action();
				transaction.Commit();
			}
		}
	}

	[TestFixture]
	public class TransactionControllerTests
	{
		private TransactionController _transactionController;
		private ISession _session;

		[SetUp]
		public void SetUp()
		{
			_session = MockRepository.GenerateStub<ISession>();
			_transactionController = new TransactionController(_session);
		}

		[Test]
		public void Begin_transaction()
		{
			StubTransaction();

			_transactionController.InTransaction(() => { });

			_session.AssertWasCalled(s => s.BeginTransaction());
		}

		[Test]
		public void Perform_action()
		{
			StubTransaction();
			var flag = false;

			_transactionController.InTransaction(() => { flag = true; });

			Assert.That(flag, Is.True);
		}

		[Test]
		public void Commit_transaction()
		{
			var transaction = StubTransaction();

			_transactionController.InTransaction(() => { });

			transaction.AssertWasCalled(t => t.Commit());
		}

		[Test]
		public void Dispose_transaction()
		{
			var transaction = StubTransaction();

			_transactionController.InTransaction(() => { });

			transaction.AssertWasCalled(t => t.Dispose());
		}

		[Test]
		public void Rollback_on_error()
		{
			var transaction = StubTransaction();

			try
			{
				_transactionController.InTransaction(() => { throw new Exception(); });
			}
			catch (Exception)
			{ }

			transaction.AssertWasNotCalled(t => t.Commit());
			transaction.AssertWasCalled(t => t.Dispose());
		}

		private ITransaction StubTransaction()
		{
			var transaction = MockRepository.GenerateStub<ITransaction>();
			_session.Stub(s => s.BeginTransaction()).Return(transaction);
			return transaction;
		}
	}

This can also be done using an AOP framework (like PostSharp) but, having tried that, I prefer it to be explicit in the code.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s