Wednesday, July 23, 2008

TransactionScope Troubleshooting

We always use TransactionScope to handle the transaction based on .Net framework. Before use it, you must add the System.Transaction reference and using it. In my project, we use the LINQ to SQL as ORM framework to access the database. It provides the transaction handling by default that is SubmitChanges() method. If you invoke it, and some errors occur, the datas which were changed will rollback. Besides, we can use the System.Data.Common.DbTransaction to handle the transaction. We can create the instance of it through by invoking the BeginTransaction() method of the connection property of DataContext, then set it to the Transaction property of DataContext. The code sippet like this:

        LinqSampleDataContext context = new LinqSampleDataContext();

        System.Data.Common.DbTransaction trans = null;

        try

        {

            context.Connection.Open();

            trans = context.Connection.BeginTransaction();

            context.Transaction = trans;

 

            context.Employees.InsertOnSubmit(emp);

            context.SubmitChanges();

 

            trans.Commit();

        }

        catch (Exception ex)

        {

            if (trans != null)

            {

                trans.Rollback();

            }

        }

 

However, the ways LINQ provides are only used in the scope of DataContext, if you want to use a couple of DataContext to execute the different operation, you have to use TransactionScope, like this:

        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))

        {

            try

            {

 

                for (int i = 0; i < nominees.Count; ++i)

                {

                    Backup newBackup = nominees[i];

                    Ticket ticket = tickets[i];

 

                    //update the information of ticket

                    //mainly add the information of employee;

                    ticket.EmployeeID = newBackup.EmployeeID;

                    ticket.HaveNominated = true;

                    ticket.IsConfirmedByManager = true;

                    ticket.Status = TicketStatus.Enroll.ToString();

 

                    ticketAccessor.Update(ticket);

                }

 

                //update the IsSubmit of backup;

                ChangeSubmitStatue(backup);

 

                //remove the record of nominee in backup table

                Delete(nominees);

            }

            catch (Exception ex)

            {

                ThrowHelper.ThrowBackupException(“Finalizing occurs an error. The transcation will be rollback.”);

                return false;

            }

 

            scope.Complete();

        }

 

Running the code, maybe you will meet an error which is an System.Data.SqlClient.SqlException, and the message of it is “MSDTC on server ‘{Server Name}’ is unavailable.” What happen? It is necessary to make sure whether the Distributed Transaction Coordinator Service is start or not. Please notice I mean you should check your database server instead of the one your software is running. You can open the Administrative Tools and Service, find the Distributed Transaction Coordinator Service and Start it. You’d better set it to be Automatic.

Let’s consider about this situation. The server your software is running is not same machine with your database server. Now running the code, the terrible expection will be thrown maybe this time. The type of it is System.Transactions.TransactionManagerCommunicationException and the message is “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.”

Please pay attention to the type of exception. It’s a communication error. Although you start the correct service in Database server, but the server your software is running can’t communicate with your database server when we use the distributed transaction. In fact, it’s a security issue. The solution to this problem is set the appropriate MSDTC Security Configuration option on Operation System such as Windows Server 2003 sp1 or Windows XP sp2.

Note: The machine you wanna set should be the one your software is going to run.

To access the MSDTC Security Configuration dialog box follow these steps:
1. Click Start, click Run, and type dcomcnfg to launch the Component Services Management console.
2. Click to expand Component Services, click to expand Computers, right click My Computer, and click Properties.
3. Click the MSDTC tab of the My Computer Properties dialog and click the Security Configuration button to display the Security Configuration dialog box.

What are the correct values? Microsoft give the recommended values refer to the table as below:

Configuration Option Default Value Recommended Value
Network DTC Access Disabled Enabled
Client and Administration    
Allow Remote Clients Disabled Disabled
Allow Remote Administration Disabled Disabled
Transaction Manager Communication    
Allow Inbound Disabled Enabled
Allow Outbound Disabled Enabled
Mutual Authentication Required Enabled Enabled if all remote machines are running Win2K3 SP1 or XP SP2 or higher, and are configured with “Mutual Authentication Required”.
Incoming Caller Authentication Required Disabled Enabled if running MSDTC on cluster.
No Authentication Required Disabled Enabled if remote machines are pre-Windows Server 2003 SP1 or pre- Windows XP SP2.
Enable TIP Disabled Enabled if running the BAM Portal.
Enable XA Transactions Disabled Enabled if communicating with an XA based transactional system such as when communicating with IBM WebSphere MQ using the MQSeries adapter.

You may refer to the figure that I post as below: securityconfig

In fact, it is default value on Windows Server 2003. However, the machine your software is running always is on Windows XP during Unit Test. If you don’t find the key to this problem, maybe you will be confused.

No comments: