开发者

How to rollback multiple Queries on different database servers in case of any error

I am using different SQL procedures in an application. First procedures insert some rows then some processing in C#code and then 2nd procedure do some updation then again some code processing then third procedure delete some record and then insert new record. When all is done on Sever 1 then data is fetch from this server and sent to Server 2 there record is deleted and new record is inserted. IF there is error at any stage on any server in any procedure i want to roll back all the record. I can not use begin trans because processing takes time and can not block table as others users are also using same tables in parallel. So kindly开发者_如何学Python tell me how can i achieve it without blocking the table for other users.

Thanks in advance.

Edited (Added code example): I tried Transaction Scope but i am getting exception while opening the connection. I configured MS DTC but may be not configured properly.

" Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool."

using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required))
    {


        try
        {
            dl.SetBookReadyToLive(13570, false);
            //SetBookReadyToLive
            dl.AddTestSubmiitedTitleID(23402);
            dl.AddBookAuthorAtLIve(13570, 1);
            ts.Complete();

        }
        catch (Exception ex)
        {
            Response.Write(ex.Message);
        }
    }

public void SetBookReadyToLive(long BookID, bool status) { try { if (dbConMeta.State != ConnectionState.Open) dbConMeta.Open();

          SqlCommand cmd = new SqlCommand("spSetBookReadyToLive", dbConMeta);
          cmd.CommandType = CommandType.StoredProcedure;
          cmd.Parameters.Clear();
          cmd.Parameters.Add("@BookID", BookID);
          cmd.Parameters.Add("@status", status);
          cmd.ExecuteNonQuery();
          if (dbConMeta.State == ConnectionState.Open)
              dbConMeta.Close();

      }
      catch
      {
          if (dbConMeta.State == ConnectionState.Open)
              dbConMeta.Close();

      }

  }

I get the exception on opening the connection of method>

I am using SQL Server 2000, i have set the configuration of MS DTC on the machine where SQL Server is installed and also on my PC from where i am running the code. But still same exception.

Kindly help me to configure it


You can use the TransactionScope class. It works generally well but in case of distributed SQL servers like in your case requires the MS DTC enabled in both servers and configured properly (security has to be granted for execution of network transactions, distributed ones and so on...)

here a copy paste from an example on MSDN, you could "almost" use it like this... :)

// Create the TransactionScope to execute the commands, guaranteeing
// that both commands can commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
    using (SqlConnection connection1 = new SqlConnection(connectString1))
    {
        // Opening the connection automatically enlists it in the 
        // TransactionScope as a lightweight transaction.
        connection1.Open();

        // Create the SqlCommand object and execute the first command.
        SqlCommand command1 = new SqlCommand(commandText1, connection1);
        returnValue = command1.ExecuteNonQuery();
        writer.WriteLine("Rows to be affected by command1: {0}", returnValue);

        // If you get here, this means that command1 succeeded. By nesting
        // the using block for connection2 inside that of connection1, you
        // conserve server and network resources as connection2 is opened
        // only when there is a chance that the transaction can commit.   
        using (SqlConnection connection2 = new SqlConnection(connectString2))
        {
            // The transaction is escalated to a full distributed
            // transaction when connection2 is opened.
            connection2.Open();

            // Execute the second command in the second database.
            returnValue = 0;
            SqlCommand command2 = new SqlCommand(commandText2, connection2);
            returnValue = command2.ExecuteNonQuery();
            writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
        }
    }

    // The Complete method commits the transaction. If an exception has been thrown,
    // Complete is not  called and the transaction is rolled back.
    scope.Complete();

}

source: TransactionScope Class

to minimize locks you could specify the IsolationLevel with the overload of the constructor which takes a TransactionScopeOptions, default is Serializable if you are fine with that you could set it to ReadCommitted.

Note: Personally I would not use this one unless absolutely needed, because it's a bit of a pain to have the DTC always configured and Distributed Transactions are in general slower than local ones but really depends on your BL / DAL logic.


Short answer : The same way you would do it if you would do it in MS SQL Management Studio.

  1. You open a connection to a server.
  2. Open a transaction for a specific server
  3. You run your queries related to this server
  4. You make sure to keep your connection alive while you... [go back to 1. for next server]

If all your queries worked, commit all your changes. Else, rollback all your queries.

Warning : The first table will most likely be locked until you're done with all your servers/queries. What you could do here to help this : If you got a lot of data, you can transfer the data to temporary tables on every servers before doing the step #2. Once this is done, you open the transaction, do your fast things, then commit/rollback asap.

Note: I know you asked how to achieve this without locking the tables, hence why I added an idea in the « warning » part.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜