5
private void btnConfigure_Click(object sender, EventArgs e)
{
    try
    {
        dbConfigure dc = new dbConfigure();
        SqlTransaction tr = conn.BeginTransaction();
        cmd.Transaction = tr;
        if (dc.configuration(cmd, ps.tableNames))
            tr.Commit();
        else
        {
            tr.Rollback();
            mesg.show("Transaction is Rolled back");
        }
    }
    catch (Exception ex)
    {
        mesg.show(ex.Message);
    }
}

If i get problem anywhere in configuration method then it returns me false and I can see the message Transaction is Rolled Back. But actually transaction is not rolled back completely and some changes in database structure made by this function stay there inspite of rollback which is quite undesired. My Question is What can be the possibility of malfunctioning of Transaction roll back?

I have nowhere else any transaction in my project except the shared (above) method

Little Details

I am calling a very lengthy/complex function configuration of my class dbConfigure. It makes some required changes in database strucure. e.g. It

  1. Drops foriegnKeys
  2. Drops Primary keys
  3. Drops auto-increment fields

    It saves these keys before dropping and recreates in desired order/position

conn is an SqlConnection which is already opened, I use no connection anywhere other than this

cmd is conn.CreateCommand() I use no command anywhere other than this

I never close the connection in this whole process, however SqlDataReader's are closed in configuration function when they do their job.

Sami
  • 8,168
  • 9
  • 66
  • 99
  • 1
    Thanks a lot @Morten Jacobsen. I did not think that way. So I got my answer. But could you please guide also if there is any way to do that? Mean roll back a create table if another create table after it fails? – Sami Nov 15 '12 at 11:59
  • 1
    Yes @GeorgeStocker. Although link you told does not seem much helpful to solve my problem. However I am happy I have learned something by this question. Best answer could be to help me to find a way to roll back structural changes in database. But at least your comment gives bit guidance and some motivation to do that. You can post it as answer and surely I am going to accept it if I do not find the better one. Because it is valid answer – Sami Nov 15 '12 at 12:10
  • I have deleted my previous comment, since as Remus Rusanu pointed out, it is actually possible to do transactional DDL in SQL Server. My comment was based on Oracle databases where it is not possible. – Morten Jacobsen Nov 15 '12 at 13:04

2 Answers2

7

Changes to database structure are not transactional, so you cannot rollback creation of a new table, for example

BS. Most DDL is transactional and can be rolled back. Only changes that involve interactions with non transactional components (like the filesystem, eg. adding a new file to a database) cannot be rolled back. Any DDL that is non transactional will also very explicitly raise an exception if is invoked in an active transaction.

Adding and modifying tables is very explicitly transactional, and it can easily be ilustrated with an example:

begin transaction;
create table foo (a int);
select * from sys.tables where object_id = object_id('foo');
rollback;
select * from sys.tables where object_id = object_id('foo');

Therefore the problem lies in the OP missing code, the parts not posted.

As a general comment one should use System.Transactions when possible (with consideration that the default constructor is broken). If using SqlConnection.BeginTransaction one still better rely on the IDisposable:

using (SqlTransaction trn = conn.BeginTransaction())
{
   ...
   trn.Commit ();
}

System.Transactions should be favored though as they do not depend on code discipline, any code SqlClient in the transaction scope will automatically enroll.

And btw have the configuration function raise on error, not return false.

And on the underlying real problem: how to handle a lenghy, complicated migration that is not possible to be enrolled in one single transaction (eg. it simply could generate too much log). The answer is that the only feasible option is to take a database backup at the beginning of the migration and restore from this backup if the migration fails. The alternative of providing a manual, tested and reliable, compensating action for every migration operation in order to undo the migration is incredibly difficult, errorprone, and ultimately unnecessary since restoring from a backup is so much simpler and provable correct.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • You are right this is possible in SQL server (I wasn't aware).. However, you should maybe point out that its vendor-specific - Oracle does not allow it as far as I know. Since the OP hasn't specified which database he is connecting to, its possible that this approach is not viable. – Morten Jacobsen Nov 15 '12 at 12:36
  • 1
    @MortenJacobsen: the question is tagged 'sql-server' and the OP code uses SqlClient components. As for the DDL transact ability, the answer is much more complex: http://stackoverflow.com/a/4736346/105929 but ultimately unnecessary to verge into since the question was so clearly targeting a specific vendor. – Remus Rusanu Nov 15 '12 at 12:42
  • I see, didn't notice the tag. – Morten Jacobsen Nov 15 '12 at 12:42
  • Good answer. I will take some time :) – Sami Nov 15 '12 at 13:11
1

Could this be done instead by using TransactionScope, rather than creating the SqlTransaction in your code. Something like this:

using (TransactionScope transaction = new TransactionScope())
{
    ...perform your database action here

    scope.Complete()
}
Jason
  • 3,599
  • 10
  • 37
  • 52
  • Might be, but I think no. As I am told that changes in structure can not be rolled back. Might be there could be some way – Sami Nov 15 '12 at 12:01