Transactions and System.Transactions


While the classes within System.EnterpriseServices make working with transactions easier, they are not the only way to define transactions with VB. Visual Basic 2005 includes a new set of classes: those within the System.Transactions namespace. As the name implies, these classes make it easier to define and work with transactions in your code. You may well ask at this point, “Why do we need two sets of classes that work with transactions?” As with many such questions, there are a couple of answers, each of which provides part of the answer.

The classes of System.Transaction, particularly the Transaction class itself, abstract the code from the resource managers participating in the transaction. While this is similar to the goal of the COM+ model described earlier, it is subtly different. The classes of Enterprise Services worked with the Distributed Transaction Coordinator (MSDTC), which in turn worked with the participating resource managers. The Transaction class does not need to work with the MSDTC and can coordinate multiple resource managers itself.

The classes of System.Transaction provide the means of creating your own resource managers. These resource managers may then participate in transactions. At first, you may balk at this prospect, wondering how you could write something that manages all the details of a transactional datastore. Aren’t the details enormous? Fortunately, the classes make it easy to enlist in a transaction and report on your results.

Creating Transactions

System.Transaction supports two means of working with transactions: implicit and explicit. With implicit transactions, you define a boundary for the transaction. Any resource managers you use within this boundary become part of the transaction. That is, if you have defined a boundary and then call a database such as SQL Server, the actions performed on the database are part of the transaction. Explicit transactions, as you may have guessed, mean that you explicitly commit or rollback the transaction as needed.

Using the implicit model can greatly simplify the code involved in a transaction. For example, the TransferMoney method used in the preceding sample could be rewritten to use an implicit transaction:

  Public Sub TransferMoney(ByVal amount As Decimal, _   ByVal sourceBank As String, _   ByVal sourceAccount As String, _   ByVal destinationBank As String, _   ByVal destinationAccount As String)     Using scope As New TransactionScope         Withdraw(sourceBank, sourceAccount, amount)         Deposit(destinationBank, destinationAccount, amount)     End Using End Sub 

The Using clause wraps the two methods within an implicit transaction. All resource managers that recognize transactions participate in this transaction. The Using clause guarantees that the TransactionScope object is disposed of when the transaction is complete.

Using explicit transactions requires a bit more code, but provides greater control over the transaction. You can use either the Transaction or the CommittableTransaction class to wrap transactions in this model. CommittableTransaction is a child class of Transaction, and adds the capability to commit a transaction, as the name implies.

Using a CommittableTransaction in the bank sample changes the TransferMoney method as follows:

  Public Sub TransferMoney(ByVal amount As Decimal, _     ByVal sourceBank As String, _     ByVal sourceAccount As String, _     ByVal destinationBank As String, _     ByVal destinationAccount As String)     Using txn As New CommittableTransaction         Withdraw(sourceBank, sourceAccount, amount, txn)         Deposit(destinationBank, destinationAccount, amount, txn)     End Using End Sub 

Notice that the Withdraw and Deposit methods now have an additional parameter to receive the transaction. These additions enable the two methods to vote on the transaction:

  Private Sub Deposit(ByVal bank As String, _   ByVal account As String, _   ByVal amount As Decimal, _   ByVal txn As CommittableTransaction)     Dim ConnectionString As String     Dim SQL As String     Dim cmdCurrent As SqlCommand     Dim currentValue As Decimal     Dim cmdUpdate As SqlCommand     ConnectionString = My.Settings.Item(bank).ToString     SQL = String.Format("SELECT Amount FROM Accounts WHERE Name = '{0}'", _       account)     If amount < 0 Then         amount = amount / 0     ElseIf amount < 50 Then         Throw New ArgumentException("Value of deposit must be greater than 50")     Else         Using conn As New SqlConnection(ConnectionString)             Try                 conn.Open()                 'join the transaction                 conn.EnlistTransaction(txn)                 'get the current value                 cmdCurrent = New SqlCommand(SQL, conn)                 currentValue = CDec(cmdCurrent.ExecuteScalar())                 SQL = String.Format("UPDATE Accounts SET Amount = " _                   "{0} WHERE Name = '{1}'", _                   currentValue + amount, account)                   cmdUpdate = New SqlCommand(SQL, conn)                 cmdUpdate.ExecuteNonQuery()                 txn.Commit()             Catch ex As Exception                 'deal with transaction here                 txn.Rollback()                 Throw New DataException("Error depositing", ex)             End Try         End Using     End If End Sub 

The principal change here is that the SQL connection must be enlisted in the transaction using the EnlistTransaction (or EnlistDistributedTransaction) method. Once it is a part of the transaction, it can then use the transaction methods to commit or rollback each part of the transaction.

Using the TransactionScope and Transaction classes can greatly decrease the amount of effort involved in creating and working with transactions in your applications. Generally, using implicit transactions using TransactionScope is easier, less error prone, and should be your first choice.

Creating Resource Managers

In addition to using the classes in System.Transactions for managing transactions, you can also use them to define your own resource managers. These resource managers can then participate in transactions with databases, MSDTC, message queues, and more. There are three basic steps in defining a resource manager:

  1. Create an enlistment class. This class is used to track the resource manager’s participation in the transaction. That is, this is the class that will vote on whether the transaction should complete or be rolled back. This class should implement the IEnlistmentNotification interface.

  2. Enlist the new enlistment class in the transaction. There are two main ways the class may participate in the transaction: EnlistDurable and EnlistVolatile. You use EnlistDurable is if your resource manager stores data permanently, such as in a file or database. EnlistVolatile is used if your resource manager stores its information in memory or in some other nonrecoverable location.

  3. Implement the methods of the IEnlistmentNotification interface to react to the states of the transaction. The IEnlistmentNotification interface provides four methods: Prepare, Commit, Rollback, and InDoubt. Commit and Rollback are self-explanatory, used at these two phases of the transaction. Prepare is called before Commit, to determine whether it is possible to commit the transaction. Finally, InDoubt is called if the transaction is questionable. This can happen if the transaction coordinator has lost track of one of the resource managers.

Why would you define your own resource managers and not simply use an existing one such as SQL Server? You might need to store data in another database that does not directly participate in transactions. Alternately, you may want to enable a normally nontransactional component with transactional behavior. For example, the cache in ASP.NET does not support adding items to the cache using transactions. You could create a resource manager that wraps the ASP.NET cache and adds support for commit and rollback of entries. This might be part of a system in which you want to use the cache as an in-memory datastore. While this would work without the transactions, adding transactional support would ensure that if the database write fails for any reason, then the entry could be rolled back out of the cache.




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net