mandag 14. desember 2009

Testing using Database and Rollback

I've several times searched the web for good examples. I've fond Examples using TranscationScope, but It gets really conplex once there are more than one connection. Causing Distributed Transaction Coordinator (MSDTC) to complain. Only solution then is to fix both client and server configuration, geeeahh. Ugly...
An other approach is using Serviced component mechanism that enables COM+ service. I feels a bit 2000'ish.

At last I decided to write my onwn code... And I feel quite prowd doing so...
I use Linq to SQL. Giving me some limitations. But this is my first shoot.

public class DbTools<T> where T : DataContext
{

   public static void ExecuteWithRollback(string connectionName, Action<T> rollbackDelegate)
   {
      Recorder.Record(rollbackDelegate).Connection(connectionName).Rollback();
   }

   public static void ExecuteWithCommit(string connectionName, Action<T> rollbackDelegate)
   {
      Recorder.Connection(connectionName).Record(rollbackDelegate).Commit();
   }

   public static Recorder<T> Recorder
   {
      get
      {
         return new Recorder<T>();
      }
   }
}

This is used as an entrypoint for my fluid syntax. logig is in the Recorder class
public class Recorder<T> where T : DataContext
{
   private string _connectionName;
   private IDbConnection _connection;
   private Action<T> _executeDelegate;

   internal Recorder()
   {
   }

   public Recorder<T> Connection(IDbConnection connection)
   {
      _connection = connection;
      return this;
   }

   public Recorder<T> Connection(string connectionName)
   {
      _connectionName = connectionName;
      return this;
   }

   public Recorder<T> Record(Action<T> executeDelegate)
   {
      _executeDelegate = executeDelegate;
      return this;
   }

   private object GetConnectionObject()
   {
      if (_connection != null)
         return _connection;

      return ConfigurationManager.ConnectionStrings[_connectionName].ConnectionString;

   }

   private void Execute(bool commit)
   {
      if (_executeDelegate == null)
         throw new RecorderException("ExecuteDelegate not set");
      if (_connection == null && string.IsNullOrEmpty(_connectionName))
         throw new RecorderException("Connection not set");

      // Hack to make new-call Work with parameters. Constraints don't cover this case
      var dataContext = (T)Activator.CreateInstance(typeof(T), GetConnectionObject());

      dataContext.Connection.Open();
      try
      {
         using (var trans = dataContext.Connection.BeginTransaction())
         {
            dataContext.Transaction = trans;
            {
               _executeDelegate(dataContext);
               if (commit)
                  trans.Commit();
               else
                  trans.Rollback();
            }
         }
      }
      catch (Exception e)
      {
         throw new Exception("Transaction might be disposed. Try to set <TEntity>.Options.TrackingMode = TrackingMode.Connected. If this don't resove the probblme theres properbly a part of the code that calls either Transaction.Dispose/Commit/Rollback or Connection.Dispose/Close", e);
      }
   }

   public void Commit()
   {
      Execute(true);
   }

   public void Rollback()
   {
      Execute(false);
   }
}

Now I can test my code.


[TestFixture]
public class DbToolsTest
{

   [Test]
   public void TestRollback()
   {
      DbTools<BNSystemDataContext>.ExecuteWithRollback("BNSystemConnectionString", MethodBodyToRollback);
   }

   [Test]
   public void TestRecordWithComplete()
   {

      DbTools<BNSystemDataContext>.Recorder.Connection("BNSystemConnectionString").Record(MethodBodyToRollback).Rollback();
   }

   private static void MethodBodyToRollback(BNSystemDataContext context)
   {
      const string socialSecurityNumber = "24129912345";
      var boPerson = new BOPerson(context) {Options = {TrackingMode = TrackingMode.Connected}};
      var p = boPerson.LoadSecurityNumber(socialSecurityNumber);
      boPerson.Update(p, "1234567");
   }

}

Ingen kommentarer:

Legg inn en kommentar