Monday, November 8, 2010

Managing distributed transactions without enabling MSDTC...

Is it possible? Short answer is no. Long answer is yes, but only to certain extend. Another stupid attempt to solve problem in a situation where developers are the lowest being in the organisation. In the perfect world where things working as expected, dealing with distributed transaction is a snap.
    ...
    using(TransactionScope scope = new TransactionScope())
    {
        using (SqlConnection conn1 ...)
        {
            ...
        }

        using (OracleConnection conn2 ...)
        {
            ...
        }

        scope.Complete();
    }
    ...
However the nightmare begin when your Server/DB admin give you a big "NO" to MSDTC but the other party insisted for data rollback if anything goes wrong during the process. So what I did was to create my own transaction manager class. Before I begin, I need to have a wrapper class that contain a connection and transaction objects, this is where the begintransaction happen. The class will be manage by the transaction manager. Technically it's just managing a group of local transactions.
    using System;
    using System.Data;
    
    public class EnlistedDBConnection : IDisposable
    {
        private IDbConnection _connection;
        private IDbTransaction _transaction;
        
        public IDbConnection Connection
        {
            get { return _connection; }
        }
        
        public IDbTransaction Transaction
        {
            get { return _transaction; }
        }
        
        public EnlistedDBConnection(IDbConnection connection)
        {
            _connection = connection;
            
            if (_connection.State == ConnectionState.Closed) _connection.Open();
            _transaction = connection.BeginTransaction();
        }
        
        internal void Commit()
        {
            _transaction.Commit();
        }
        
        internal void Rollback()
        {
            _transaction.Rollback();
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (_connection != null && _connection.State == ConnectionState.Open) _connection.Close();
            if (_connection != null) _connection.Dispose();
            if (_transaction != null) _transaction.Dispose();
        }

        #endregion
    }
Code for the transaction manager. Before commiting, I need to ensure all connections are alive, if any of it down, cancel everything. However there's still one worst case scenario that this code couldn't handle, after done with connection checking, while committing suddenly one connection down in the process. It is not possible to rollback the committed transaction. So chances for orphan data are still there.
    using System;
    using System.Data;
    using System.Collections.Generic;
    
    public class BasicTransactionManager : IDisposable
    {
        private bool _isCommited;
        private List<EnlistedDBConnection> _enlistedConnections;
    
        public BasicTransactionManager() : this(new List<EnlistedDBConnection>())
        {
        }
        
        private BasicTransactionManager(List<EnlistedDBConnection> enlistedConnections)
        {
            _enlistedConnections = enlistedConnections; 
        }
        
        public EnlistedDBConnection Enlist(IDbConnection connection)
        {
            EnlistedDBConnection item = new EnlistedDBConnection(connection);
            _enlistedConnections.Add(item);
            
            return item;
        }
        
        public void Complete()
        {
            Commit();
        }
        
        /// 
        /// While in the loop, worst case scenario that this logic couldn't handle is that
        /// if the first transaction committed, then the second one failed, chances for orphan data
        /// to occur are there because we couldn't rollback something that has been commited.
        /// 
        private void Commit()
        {
            // if one of the db connection failed, cancel everything
            if(!VerifyConnection()) throw new Exception("DB connection failed.");
            
            string message = String.Empty;
            foreach (EnlistedDBConnection enlistedConnection in _enlistedConnections)
            {
                try
                {
                    enlistedConnection.Commit();
                }
                catch(Exception ex)
                {
                    message += String.Format("{0}\r\n", ex.Message);
                }
            }
            if(!String.IsNullOrEmpty(message)) throw new Exception(message);
            
            _isCommited = true;
        }
        
        private void Rollback()
        {
            string message = String.Empty;
            foreach (EnlistedDBConnection enlistedConnection in _enlistedConnections)
            {
                if(enlistedConnection.Connection != null && enlistedConnection.Connection.State == ConnectionState.Open)
                {
                    try
                    {
                        enlistedConnection.Rollback();
                    }
                    catch (Exception ex)
                    {
                        message += String.Format("{0}\r\n", ex.Message);
                    }
                }
            }
            if (!String.IsNullOrEmpty(message)) throw new Exception(message);
        }
        
        private bool VerifyConnection()
        {
            bool _allOpened = true;
            
            foreach(EnlistedDBConnection enlistedConnection in _enlistedConnections)
            {
                if(enlistedConnection.Connection == null || enlistedConnection.Connection.State != ConnectionState.Open)
                {
                    _allOpened = false;
                    break;
                }
            }
            
            return _allOpened;
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (!_isCommited) Rollback();
            
            _enlistedConnections.ForEach( delegate(EnlistedDBConnection item) { item.Dispose(); } );
            // _enlistedConnections.ForEach( item => item.Dispose() );
        }

        #endregion
    }
This is how I use it in my business object. EnlistedDBConnection exposed connection and transaction property. That's how I obtain the instance and pass it to Command object.
    using (BasicTransactionManager transaction = new BasicTransactionManager())
    {
        EnlistedDBConnection dbcon1 = transaction.Enlist(/* your db connection object */);
        EnlistedDBConnection dbcon2 = transaction.Enlist(/* your db connection object */);

        ...

        transaction.Complete();
    }
Note:
This solution is not by any mean to be a replacement to MSDTC (not even in your wet dream). However, it is better to have something rather than nothing. I'd rather use this solution instead of executing another command to undo the changes manually which I feel ridiculous.

Saturday, November 6, 2010

JQuery pluggins summary and best practices...

Writing jQuery plugins allows you to make the most out of the library and abstract your most clever and useful functions out into reusable code that can save you time and make your development even more efficient. Here's a brief summary of the post and what to keep in mind when developing your next jQuery plugin:
  • Always wrap your plugin in (function( $ ){ // plugin goes here })( jQuery );
  • Don't redundantly wrap the this keyword in the immediate scope of your plugin's function
  • Unless you're returning an intrinsic value from your plugin, always have your plugin's function return the this keyword to maintain chainability.
  • Rather than requiring a lengthy amount of arguments, pass your plugin settings in an object literal that can be extended over the plugin's defaults.
  • Don't clutter the jQuery.fn object with more than one namespace per plugin.
  • Always namespace your methods, events and data.
  • jQuery.fn is pronounced jQuery effin'

Monday, November 1, 2010

Compress and Decompress text using System.IO.Compression...

I'm lazy today. This code was copied directly from internet for my personal reference.
    using System;
    using System.Text;
    using System.IO;
    using System.IO.Compression;

    public static class GZipStreamUtility
    {
        public static string Compress(string text)
        {
            if (String.IsNullOrEmpty(text)) return String.Empty;

            byte[] buffer = Encoding.UTF8.GetBytes(text);
            MemoryStream ms = new MemoryStream();
            using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
            {
                zip.Write(buffer, 0, buffer.Length);
            }

            ms.Position = 0;
            MemoryStream outStream = new MemoryStream();

            byte[] compressed = new byte[ms.Length];
            ms.Read(compressed, 0, compressed.Length);

            byte[] gzBuffer = new byte[compressed.Length + 4];
            System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
            System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);

            return Convert.ToBase64String(gzBuffer);
        }

        public static string Decompress(string compressedText)
        {
            if (String.IsNullOrEmpty(compressedText)) return String.Empty;

            byte[] gzBuffer = Convert.FromBase64String(compressedText);
            using (MemoryStream ms = new MemoryStream())
            {
                int msgLength = BitConverter.ToInt32(gzBuffer, 0);
                int length = gzBuffer.Length - 4;
                ms.Write(gzBuffer, 4, length);

                byte[] buffer = new byte[msgLength];

                ms.Position = 0;
                using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
                {
                    zip.Read(buffer, 0, buffer.Length);
                }

                return Encoding.UTF8.GetString(buffer);
            }
        }
    }

Friday, October 29, 2010

Retrieving checkboxes values using JQuery...

I have an html like this in my page
    ...
    <div id="container1">
        <input type="checkbox" value="1" /> a
        <input type="checkbox" value="2" /> b
        <input type="checkbox" value="3" /> c
        <input type="checkbox" value="4" /> d
        <input type="checkbox" value="5" /> e
        <input type="checkbox" value="6" /> f
    </div>

    <div id="container2">
        <input type="checkbox" value="11" /> a1
        <input type="checkbox" value="12" /> b1
        <input type="checkbox" value="13" /> c1
        <input type="checkbox" value="14" /> d1
        <input type="checkbox" value="15" /> e1
        <input type="checkbox" value="16" /> f1
    </div>
    <input id="mybutton" type="button" value="click me" />
    ...

Demo

container1
a b c d e f

container2
a1 b1 c1 d1 e1 f1

result


Now what I want is, whenever I click the button I want to retrieve all the values from the checked checkboxes. This is pretty easy in JQuery. Here's how my JS will look like
    ...
    $("#mybutton").click( function() {
        var myarray = [];
        
        $('input:checkbox:checked').each( function() {
            myarray.push($(this).val());
        });
        
        alert(myarray.join(', '));
    } );
    ...
What if I want it only from the first <div>? I just need to pass context to the selector
    ...
    $("#mybutton").click( function() {
        var myarray = [];
        
        // passing container1 as context to selector
        $('input:checkbox:checked', '#container1').each( function() {
            myarray.push($(this).val());
        });
        
        alert(myarray.join(', '));
    } );
    ...
JQuery makes my life a lot easier :).

Tuesday, October 26, 2010

The beauty of JQuery...

Query by element tag name
    $("div")
Query by element id
    $("#myid")
Query by css class name
    $(".css-whatever")
Query by input type
    $("input:checkbox")
Query by a set of element ids
    $("#myid1,#myid2,#myid3")
Query by input type but exclude the one with 'whatever' id
    $("input:checkbox:not(#whatever)")

Monday, October 18, 2010

Creating DBHelper that can support various types of database...

Yes I know there's a framework (EF, NH and whatever name that I've never heard before) that can solve the problem. Unfortunately, EF only works on new technologies (at least with .NET framework 3.5). What if we're stucked with an old Microsoft technologies (NH is another option, but I still couldn't figure out on how and where to begin with - ~ I ain't a .NET superstar ~)? Is it possible to do it then? Yes!!! back to basic stuff!!!

I've try it on .NET framework 2.0. IDBConnection, IDBTransaction and IDBDataParameter are available in System.Data namespace since .NET framework 1.1 however, DBDataReader in System.Data.Common was available not until .NET framework 2.0 (DBDataReader was derived by SqlDataReader, OracleDataReader and OleDbDataReader). Those are the important interfaces and class needed.

Basically we need to create an abstract layer to our db helper. Let's just call it IDBHelper. Define all basic methods that can support various databases. It's important not to have a method that is specific to any data provider such as ExecuteXmlReader.
    ...
using System.Data;
using System.Data.Common;

public interface IDbHelper
{
IDbConnection CreateConnectionInstance(string connectionString);

...

DbDataReader ExecuteReader(IDbConnection connection, string query, CommandType cmdType, params IDbDataParameter[] commandParameters);

...

IDbDataParameter CreateParam(string paramName, object paramValue);
}
For concrete implementation. Create a class that implement IDBHelper interface. I named the class as SqlClientHelper.
    ...
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;

public class SqlClientHelper : IDbHelper
{
public IDbConnection CreateConnectionInstance(string connectionString)
{
return new SqlConnection(connectionString);
}

...

public DbDataReader ExecuteReader(IDbConnection connection, string query, CommandType cmdType, params IDbDataParameter[] commandParameters)
{
...
}

...

public IDbDataParameter CreateParam(string paramName, object paramValue)
{
SqlParameter param = new SqlParameter(paramName, paramValue);
...
}

...
}
Then, the code for OracleClientHelper.
    ...
using System;
using System.Data;
using System.Data.OracleClient;
using System.Data.Common;

public class OracleClientHelper : IDbHelper
{
public IDbConnection CreateConnectionInstance(string connectionString)
{
return new OracleConnection(connectionString);
}

...

public DbDataReader ExecuteReader(IDbConnection connection, string query, CommandType cmdType, params IDbDataParameter[] commandParameters)
{
...
}

...

public IDbDataParameter CreateParam(string paramName, object paramValue)
{
OracleParameter param = new OracleParameter(paramName, paramValue);
...
}

...
}
Noticed that the instantiation of connection and parameter object has been done in the class itself. Ideally, if we don't use any specific data provider namespaces in DAL layer, it should be flexible enough to use any database without the need to change the code logic. The concern is more on the abstract layer method signature.

Create an object factory that'll return the helper instance. This is the only place that we need to change if the project owner aka our client suddenly decided to use different data provider in future (reality is cruel).
    ...
public static class SomeObjectFactory
{
public static IDBHelper GetHelperInstance()
{
return new SqlClientHelper();

// uncomment the code below for oracle client
// return new OracleClientHelper();
}
}
The code at DAL
    ...
using (IDbConnection connection = SomeObjectFactory.GetHelperInstance().CreateConnectionInstance(connectionString))
{
connection.Open();

string query = "SELECT * FROM tblSomething WHERE Id = @Id";
IDbDataParameter param = MyObjectFactory.GetDBHelperInstance().CreateParam("@Id", id);

DBDataReader reader = SomeObjectFactory.GetHelperInstance().ExecuteReader(connection, query, CommandType.Text, param);

...
}
...
This way, adding System.Data and System.Data.Common namespace should be sufficient enough to our DAL.

Thursday, October 14, 2010

Creating custom event in JavaScript...

First, define custom event.
    var CustomEvent = function() {
//name of the event
this.eventName = arguments[0];
var mEventName = this.eventName;

//function to call on event fire
var eventAction = null;

//subscribe a function to the event
this.subscribe = function(fn)
{
eventAction = fn;
};

//fire the event
this.fire = function(sender, eventArgs)
{
if(eventAction != null)
{
eventAction(sender, eventArgs);
}
else
{
alert('There was no function subscribed to the ' + mEventName + ' event!');
}
};
};
Then create an event handler.
    var myEvent = new CustomEvent("helloworld event");
myEvent.subscribe(event_triggered);

function event_triggered(sender, eventArgs)
{
alert("hello world");
}
Now, the triggering part.
    ...
if(typeof(myEvent) != "undefined")
{
myEvent.fire(null, {
message: 'you just witnessed the firing of a custom event called ' + this.eventName + '!'
});
}
...