Booting my computer this morning I faced the exact problem for a project I am working on. I had some ideas which lead to the following design - and comments would be more than awesome. Unfortunately the design suggested by Josh isn't possible, as I have to work with a remote SQL server and can't enable the Distribute Transaction Coordinator service it relies on.
My solution is based on a few yet simple changes to my existing code.
First, I have all my repositories implement a simple marker interface:
/// <summary>
/// A base interface for all repositories to implement.
/// </summary>
public interface IRepository
{ }
Secondly, I let all my transaction enabled repositories implement the following interface:
/// <summary>
/// Provides methods to enable transaction support.
/// </summary>
public interface IHasTransactions : IRepository
{
/// <summary>
/// Initiates a transaction scope.
/// </summary>
void BeginTransaction();
/// <summary>
/// Executes the transaction.
/// </summary>
void CommitTransaction();
}
The idea is that in all my repositories I implement this interface and add code which introduces transaction directly depending on the actual provider (for fake repositories I have made a list of delegates which gets executed on commit). For LINQ to SQL it would be easy to make implementations such as:
#region IHasTransactions Members
public void BeginTransaction()
{
_db.Transaction = _db.Connection.BeginTransaction();
}
public void CommitTransaction()
{
_db.Transaction.Commit();
}
#endregion
This of course requires that a new repository class is created for each thread, but this is reasonable for my project.
Each method using the repository needs to invoke the BeginTransaction()
and the EndTransaction()
, if the repository implements IHasTransactions
. To make this call even easier, I came up with the following extensions:
/// <summary>
/// Extensions for spawning and subsequently executing a transaction.
/// </summary>
public static class TransactionExtensions
{
/// <summary>
/// Begins a transaction if the repository implements <see cref="IHasTransactions"/>.
/// </summary>
/// <param name="repository"></param>
public static void BeginTransaction(this IRepository repository)
{
var transactionSupport = repository as IHasTransactions;
if (transactionSupport != null)
{
transactionSupport.BeginTransaction();
}
}
public static void CommitTransaction(this IRepository repository)
{
var transactionSupport = repository as IHasTransactions;
if (transactionSupport != null)
{
transactionSupport.CommitTransaction();
}
}
}
Comments are appreciated!