System. Transactions C# Help

The namespace System. Transactions has been available since .NET 2.0 and brings a new  ransaction programining model to .NET applications. Figure 22-3 shows a Visual Studio class diagram with the
transaction classes, and their relationships, from the System. Transactions namespaces Transaction, CommittableTransaction,DependentTransaction,andsubordinateTransaction.

Transaction is the base class of all transaction classes and defines properties, methods, and events available with all transaction classes. CommittableTransaction is the only transaction class that supports . committing, This class has a Commit () method; all other transaction classes can do only a rollback. The cIass DependentTransaction is used with transactions that are dependent on another transaction. A dependent transaction can depend on a transaction created from the committable transaction. Then the dependent transaction adds to the outcome of the committable transaction whether or not it is successful.

The class SubordinateTransaction is used in conjunction with the Distributed Transaction Coordinator (DTC).This class represents a transaction that is not a root transaction but can be managed by the DTe.

Figure 22-3

Figure 22-3

The following table describes the properties and methods of the Transaction class

For demonstrating the features of System. Transaction, the class Utili ties inside a separate assembly offers some static methods. The method AbortTx () returns true or false depending on the input from the user. The method DisplayTransaction1nformation () gets a Transaction1nformation object as parameter and displays all the information from the transaction: creation time, status, local, and distributed identifiers:

Committable Transactions

Thefransaction class cannot be committed programmatically; it does not have a method to commit the’lansaction. The base class Transaction just supports aborting the transaction. The only transaction class that supports a commit is the class CommittableTransaction.

With ADO.NET, a transaction can be enlisted with the connection. To make this possible, an AddStudent () method is added to the class StudentData that accepts a System. Transactions. Transaction object as second parameter. The object tx is enlisted with the connection by calling the method EnlistTransaction of the SqlConnection class. This way, the ADO.NET connection is associated with the transaction.

In the Main () method of the console application conuni t tableTransaction, first a transaction of type Conuni’:,:ableTransaction is created, and information is shown on the console. Then a Student object is created, and this object is written to the database from the AddStudent () method. If you verify the reco-d in the database from outside of the transaction, you cannot see the student added until the transaction is completed. In case the transaction fails, there is a rollback, and the student is not written to the database. Afn-r the AddStuc!ent () method is invoked, the helper method Utili ties. AbortTx () is called to ask if the transaction should be aborted. If the user aborts, an exception of type ApplicationException is thrown and, in the catch block, a rollback with the transaction is done by calling the method Rol ..back () of the Transaction class. The record is not written to the database. If the user does not abort, the Conunit () method commits the transaction, and the final state of the transaction is committed. static void Main()
(
CommittableTransaction tx = new CommittableTransaction();
Utilities.DisplayTransactionInformation(“TX created”,
tx.Transactionlnformation);
try
(
Student s1 new Student();

sl.FirstName = ‘Neno’;
sl.LastName = ‘Loye’;
sl.Company = ‘thinktecture’;
StudentData db = new StudentData();
db.AddStudent(sl, tx);
if (Utilities.AbortTx())

Here, you can see the output of the application where the transaction is active and has a local identifier. The output of the application that follows shows the result with the user choice to abort the transaction. After the transaction is finished, you can see the aborted state.

TX created
Creation Time; 7:30:49 PM
Status: ActiVO!!
Local 1D: bdcflcdc-a67e-4ccc-9aSc-cbdfeOfe9177:1
Distributed 1D: 00000000-0000-0000-0000-000000000000

ed
Creation Time: 7:30:49 PH
Status: Aborted ~
Local 1D: bdcf1cdc-a67e-4ccc-9aSc-cbdfeOfe9177:1
Distributed 1D: 00000000-0000-0000-0000-009000900000
Press any key to continue …
Abort the Transaction (y/n)? y

With the second output of the application that you can see here, the transaction is not aborted by the user. The transaction has the status committed, and the data is written to the database.

TX Created
Creation Time: 7:33:04 PH
Status: Active
Local 1D: 708bda71-fa24-46a9-86b4-18b83l20f6af:1

Distributed ID: 00000000-0000-0000-0000-000000000000
Abort the Transaction (y/n)? n
TX completed
Creation Time: 7:33:04 PM
Status: Committed
Local ID: 70Bbda71-fa24-46a9-B6b4-1BbB3120f6af:1
Distributed ID: 00000000-0000-0000-0000-000000000000
Press any key to continue …

Transaction Promotion

System. Transactions supports promotable transactions. Depending on the resources that participate with the transaction, either a local or a distributed transaction is created. SQL Server 2005 and 2008 support promotable transactions. So far you have seen only local transactions. With the samples until now, the distributed transaction ID was always set to 0, and only the local ID was assigned. With a resource that does not support promotable transactions, a distributed transaction is created. If multiple resources are added to the transaction, the transaction may start with a local transaction and promote to a distributed transaction as required. Such a promotion happens when multiple SQL Server database connections are added to the transaction. The transaction starts as a local transaction and then is promoted to a distributed transaction.

The console application is now changed in that a second student is added by using the same transaction object tx. Because every AddStudent () method opens a new connection, two connections are associated with the transaction after the second student is added.

Running the application now, you can see that with the first student added the distributed identifier is 0, but with the second student added the transaction was promoted, so a distributed identifier is associated with the transaction.

TX created
Creation Time: 7:56:24 PM
Status: Active
Local ID: Od2f5ada-32aa-40eb-b9d7-cc6aa9a2a5~4:1
Distributed ID: 00000000-0000-0000-0000-0000000000

2nd connection .~listed
Creation Time: 7:56:24 PM
Status: Active
Local ID: Od2f5ada-32aa-40eb-b9d7-cc6aa9a2a554:1
Distributed ID: 70762617-2ee8-4d23-aa87-6ac8c1418bdfd

Abort the Transaction (y/n)?

Transaction promotion requires the Distributed Transaction Coordinator (DTC) to be started. If promoting transactions fails with your system, verify that the DTC service is started. Starting the Component ServicesMMC”‘Snap-in,you can see the actual status of all DTC transactions running on your system. Byselecting Transaction List on the tree view, you can see all active transactions. In Figure 22-4, you can see that there is a transaction active with the same distributed identifier as was shown with the console output earlier. If you verify the output on your system, make sure that the transaction has a timeout and aborts in case the timeout is reached. After the timeout, you cannot see fr e transaction in the transaction list anymore. You can also verify the transaction statistics with the same tool. Transaction Statistics shows the number of committed and aborted transactions.

Figure 22-4

Figure 22-4

You can start the Componmt Services MMC snap-in by starting the Microsoft Managemmt Console (mmc. exe) application, selecting the menu File¢ Add(Remove Snap-In, and selecting Component StT\)\CtS from the list of snap-ins.

Depende”‘lransactlons

With dependent transactions, you can influence one transaction from multiple threads. A dependent transaction depends on another transaction and influences the outcome of the transaction.

The sample application DependentTransactions creates a dependent transaction for a new thread. The method TXThread () is the method of the new thread where a DependentTransaction object is passed as a parameter. Information about the dependent transaction is shown with the helper method DisplayTransactionlnformation (). Before the thread exits, the Complete () method of the dependent transaction is invoked to define the outcome of the transaction. A dependent transaction can define the outcome of the transaction by calling either the Complete () or Rollback () method. The Complete () method sets the success bit. If the root transaction finishes, and if all dependent transactions have set the success bit to true, the transaction commits. If any of the dependent transactions set the abort bit by invoking the Rollback () method, the complete transaction aborts.

With the Main () method, first a root transaction is created by instantiating the class Commit tableTransaction, and the transaction information is shown. Next, the method tx. DependentClone () creates a dependent transaction. This dependent transaction is passed to the method Tx’rhread () that is defined as the entry point of a new thread.

The method DependentClone () requires an argument of type DependentCloneOption, which is an enumeration with the values BlockCommi tUnti lComplete and RollbackIfNotComplete. This option is important if the root transaction completes before the dependent transaction. Setting the option to RollbackIfNotComplete, the transaction aborts if the dependent transaction didn’t invoke the Complete () method before the Commit () method of the root transaction. Setting the option to BlockCommitUntilComplete, the method Commit () waits until the outcome is defined by all dependent transactions.

Next, the Commit () method of the Commit tableTransaction class is invoked if the user does not abort the transaction.

C#19 , “Threading and Synchronization,” covers threading

With the output of the application,you can see the root transaction with itsidentifier.Because of the option DependentCloneOption. BlockCommi tUntilComplete, the root transaction waits in the Commi t () method untilthe outcome of the dependent transaction isdefined. As soon as the dependent transaction isfinished,the transaction iscommitted.

Root TX created
Creation Tim~: 8:35:25 PM
Status: Acti~
Local ID: 50l26e07-cd28-4eOf-a21f-a81a8e14ala8:1
Distributed ID: 00000000-0000-0000-0000-0000000000
Abort the Transaction (y/n)? n

Dependent Transaction
Creat~on Time: 8:35:25 PM
Status: Active
Local 10: 50126e97-cd28-4eOf-a21f-a81a8e14ala8:1
Distributed 10: 00000000-0000-0000-0000-0000000000
Dependent TX Complete
Root TX finished
Creation Time: 8:35:25 PM
Status: Committed
Local 10: 50126e07-cd28-4eOf-a21f-a81~8e14ala8:1
Distributed 10: 00000000-0000-0000-0000-0000000000
Creation Time: 8:35:25 PM
Status: Committed
Local 1D: 50126e07-cd28-4eOf-a21f-a81a8e14ala8:1
Distributed 10: 00000000-0000-0000-0000-0000000000 ”
Press any key to continue ..,

AmbIent TransactIons

The really big advantage of System. Transactions is the ambient transactions feature. With ambient . ~ansactions, there is no need to manually enlist a connection with a transaction; this is done abtQE”‘tically from the resources supporting ambient transactions. An ambient transaction is associated with the current thread. You can ger and set the ambient transaction with the static property Transaction. Current. APIs supporting ambient transactions check this property to get an ambient transaction, and enlist with the transaction. ADO.NET connections support
ambient tr nsactions  ‘~’1;Youcan create a onunittableTransaction object and assign it to the property Transaction. Current to initialize the ambient transaction. Another way to create ambient transactions is with the TransactionScope class. The constructor of the TransactionScope creates an ambient transaction. Because of the implemented interface IDisposable, you can use a transaction scope easily with the using statement.

The members of TransactionScope are listed in the following table.

Because the TransactionScope class implements the IDisposable interface, you can define the scope with the using statement. The default constructor creates a new transaction. Immediately after creating the TransactionScope instance, the transaction is accessed with the get accessor of the property Transaction. Current to display the transaction information on the console. Toget the information when the transaction is completed, the method OnTransactionCompleted (l is set to the TransactionCompleted event of the ambient transaction. Then a new Student object is created and written to the database by calling the StudentData.AddStudent () method. With ambient transactions, it is no longer neces,saryto pass a Transaction object to this method because the SqlConnection class supports ambient transactions and automatically enlists it with the connection: Then the Complete () method of the TransactionScope class sets the success bit. With the end of the using statement, the
TransactionScope is disposed, and a commit is done. If the Complete () method is not invoked, the Dispose () method abons the transaction.

If an ADO.NET connection should not enlist with an ambient transaction, you can set the value Enlist=false with the connection string.

Running the application, you can see an active ambient transaction afteran instance of the TransactionScope classiscreated.The lastoutput of the application isthe output from the TransactionCompleted event handler to display the finished transaction state

Ambient TX created
Creation Time: 9:55:40 PM
Status: Active
Local ID: a06df6fb-7266-435e-b90e-f024f1d6966e:1
Distributed 1D: 00000000-0000-0000-0000-0000000000
Abort the Transaction (y/n)? n
TX ccmpleted
Creation Time: 9:55:40 PM
Status: Committed
Local ID: a06df6fb-7266-435e-b90e-f024f1d6966e:l
Distributed 1D: 00000000-0000-0000-0000-0000000000
Press any key to continue …

Nested Scopes with Ambient Transactions

With the TransactionScope classyou can also nest scopes. The nested scope can be directlyinside the scope or within a method that isinvoked from a scope. A nested scope can use the same trans,action as

the outer scope, suppress the transaction, or create a new transaction that is independent from the outer scope. The requirement for the scope is defined with a TransactionScopeOption enumeration that is passed to the constructor of the TransactionScope class. The values available with the TransactionScopeOpticn enumeration and their functionality are described in the following table.

The ne,..t.sample defines two scopes, in which the inner scope IS configured to require a new transaction with the option Transac t ionScopeOpt ion. RequiresNew

Running the application, you can see that both scopes have different transaction identifiers, although the same thread is used. Having one thread with different ambient transactions because of different scopes, the transaction identifier differs in the last number following the GUID

A GUlD is a globally unique identifier consisting of a 128-bit unique value

Ambient TX created
Creation Time: 11:01:09 PM
Status: Active
Local 10: 54ac1276-5c2d-4159-84ab-36b0217c9c84:1
Distributed 10: 00000000-0000-0000-0000-0000000000

Inner Transaction Scope
Creation Time: 11:01:09 PM
Status: Active
Local 10: 54ac1276-5c2d-4159-84ab-36b0217c9c84:2
Distributed 10: 00000000-0000-0000-0000-0000000000

TX completed
Creation Time: 11:01:09 PM
Status: Committed
Local 10: 54ac1276-5c2d-4159-84ab-36b0217c9c84:2
Distributed 10: 00000000-0000-0000-0000-0000000000

completed
Creation Time: 11:01:09 PM
Status: Committed
Local IO: 54ac1276-5c2d-4159-84ab-36b0217c9c84:1
Oistribu~ed 10: 00000000-0000-0000-0000-0000000000

If you change’ the inner scope to the setting TransactionScopeOption. Required, you will find that both scopes are using the same transaction, and both scopes influence the outcome of the transachon

Multlthreadln~ with Ambient Transactions

If multiple threads should use the same ambient transaction, you need to do some extra work. An ambient transaction is bound to a thread, so if a new thread is created, it does not have the ambient transaction from the starter thread.

This behavior is demonstrated in the next example. In the Main () method, a Transacti onScope is created. Within this transaction scope, a new thread is started. The main method of the new thread ThreadMethod () creates a new transaction scope. With the creation of the scope, no parameters are passed, and therefore, the default option TransactionScopeOption. Required gets into play. If an ambient transaction exists, the existing transaction is used. If thefi is no ambient transaction, a new transaction is created.

As you start the application, you can see that the transactions from the two threads are completely’ independent. The transaction from the new thread has a different transaction ID. The transaction ID differs by. the last number after the GUID in the same way as you have seen with nested scopes when the nested scope required a new transaction.

Main thread TX
Creation Time: 21:41:25
Status: Active
Local ID: f1e736ae-84ab-4540-b71e-3de272ffc476:1
Distributed ID: 00000000-0000-0000-0000-000000000000

TX completed
Creation Time: 21:41:25
Status: Committed
Local 10: f1e736ae-84ab-4540-b71e-3de272ffc476:1
Distributed 10: 00000000-0000-0000-0000-000000000000

Thread TX
Creation Time: 21:41:25
Status: Active
Local ID: f1e736ae-84ab-4540-b71e-3de272ffc476:2
Distributed 10: 00000000-0000-0000-0000-000000000000

TX completed
Creation Time: 21:41:25
Status: Committed
Local 10: fle736ae-84ab-4S40-b71e-3de272ffc476:2
Distributed 10: 00000000-0000-0000-0000-000000000000

To use the same,ambient transaction in another thread, you need the help of dependent transactions. Now the sample is chSnged to pass a dependent transaction to the new thread. The dependent transaction is created from the ambient transaction by calling the DependentClone () method on the ambient transaction. With this method, the setting DependentCloneOption. BlockCorranitUntilComplete is set so that the calling thread waits until the new thread is completed before committing the transaction.

In the method of the thread, the dependent transaction that is passed is assigned to the ambient transaction by using the set accessor of the Transaction. Current property. Now the transaction scope is using the same transaction by using the dependent transaction. When you are finished using the dependent transaction, you need to invoke the Complete () method of the DependentTransaction object.

static void ThreadMethod(object dependentTx)

Running the application now, you can see that the main thread and the newly created thread are using, and influencing, the same transaction. The transaction Ii1tedby the threads has the same identifier. If with one thread the success bit isnot set by calling the Complete () method, the complete transaction aborts.

Main thread TX
Creation Time: 23:00:57
Status: Active
Local 1D: 2fblb54d-61f5-4d4e-a55e-f4age04776be:l
Distributed 10: 00000000-0000-0000-0000-000000000000

Thread TX
Cseation Time: 23:00:57
Status: ActiveLocal 1D: 2fblb54d-61fS-4d4e-aS5e-f4age04778be:l

Distributed :D: 00000000-0000-0000-0000-000000000000
TX completed
Creation Time: 23:00.57
Status: Committed

Local 1D: 2fblb54d-61f5-4d4e-aSSe-f4age04778be,:

Distributed 1D: OOOOOOOO-OOOO-OOOC-OOOO-OOOOOOCOOOOO
TX completed
Creation Time: 23:00:57
Status: Committed
Local 1D: 2fblb54d-61fS-4d4e-a5Se-f4age04778be’1
Distributed 10: OOOOOOOO-OOOO-OOOO-OOOO-OOOOOOOOOOCO

Posted on October 31, 2015 in Transactions

Share the Story

Back to Top