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");
   }

}

mandag 9. november 2009

Sort your Arrays

Sometimes simple things get complex and visa/versa. I've done sorting of arrays many times. Finally it struck me. I've been wasting time again... Just define you sorting algoritm in the CompareTo method. Never return any default values like 1, 0 or -1. It will make you code fail...

public class Item : IComparable<item>
{
   
    public Item(int id, string name)
    {
        Id = id;
        Name = name;
    }

    public int Id
    { get; set; }

    public string Name
    {get; set;}

    public string[] Email
    { get; set; }

    public int CompareTo(Item item)
    {
        var compare = Name.CompareTo(item.Name);
        if (compare != 0)
            return compare;

        return Id.CompareTo(item.Id);
    }
}

Now I test my code...

[Test]
public void SortArray()
{
var array = new[] {new Item(1, "B"), new Item(2, "C"), new Item(3, "A")};

Array.Sort(array);

Assert.AreEqual(array[0].Name, "A");
Assert.AreEqual(array[1].Name, "B");
Assert.AreEqual(array[2].Name, "C");
}

XmlSerializer

Every project these days tend to deal with some kind of serialization. I've Created a small pice of code to make my world simpler. How to create a Generic XmlSerializer.
public static class GenericXmlSerializer { public static T Deserialize(string xml) { var serializer = new XmlSerializer(typeof(T)); using (var sr = new StringReader(xml)) { return (T)serializer.Deserialize(sr); } } public static string Serialize(T source) { var serializer = new XmlSerializer(typeof(T)); using (var sr = new StringWriter()) { serializer.Serialize(sr, source); return sr.ToString(); } } }
My next problem is how to run my code. I've created a small example class for my example
public class Item
  {
  public string Name
  {get; set;}

  [XmlElement]
  public string[] Email
  { get; set; }

}
My production code for serialize/deserialize an instance of Item is displayed below.
--Serialize
var item = new Item {Name = "Authovr", Email = new string[] {"author1@gmail.com", "author2@gmail.com"}};
var xml = GenericXmlSerializer.Serialize(item);
--Deserialize
var xml = "<item><name>Author</name><email>author1@gmail.com</email><email>author2@gmail.com</email></item>";
var item = GenericXmlSerializer.Deserialize(xml);
Note!!
My example includes serializing an Array element. Using the XmlElement atteribute makes the serialization nice and readable.