Jim Johnson showed how System.Transacations will allow developers to use Transactions to write more reliable software in simple way that will work across different transaction coordinators while ensuring that performance cost of distributed transactions is only paid if the code uses them. I had a great time talking with Jim at the last PDC and it was exciting to see him demonstrating this technology that will ship with .NET 2.0, as well as a demo of the transactional file system that's coming with Longhorn Server.
Transactions: Ensuring reliability and resilience
Jim spoke about the philosophy behind the System.Transactions work. He views transactions as a reliability aid that helps developer write resilient applications that can recover from errors and return to a consistent state, even in the face of scalability and concurrency challenges.
Jim showed two methods that were both written to correctly handle multithreading and concurrency, but that they may not work correctly if one depends on the other, since it would require code to trap errors and handle rollbacks. He argued that transactions provide a simple way of ensuring that the two could work together in an atomic way, behaving correctly even in the case of errors, without a lot of complex code, which is the major use case for System.Transactions.
System.Transactions: an efficient, unified managed code object model
The goals behind System.Transaction are to make transactions ubiquitous. In order to do that they had to make using transactions simpler for developers and overcome the perception that they were too slow. They have answered the complexity problem by designing a managed code object model in System.Transactions that uses the same code to work with both local in-memory transactions and distributed transactions. They have reduced the performance issues by ensuring that transactions only enlist the necessary resource managers.
Using a transaction involves some fairly simple code:
using (TransactionScope scope = new TransactionScope())
{
// Do something
// Ensure that the transaction doesn't roll back
Scope.Complete()
}
The using statement provides a convenient language wrapper around the use of the TransactionScope object. The developer only has to explicitly signal the success of the transaction, otherwise the transaction will automatically be rolled back once the using statement block exits. The benefit to developers is that the code can be dramatically simplified since the developer doesn't have to write special-case clean up code in the catch block to recover from a problem.
The same code can be used to work with in-memory transactions and database or MSMQ transactions. With .NET 2.0 it is possible to use transactions without having to use the MSDTC, which makes transactions incredibly fast. If the code uses resources that involve distributed transaction coordinators these will automatically be brought in. So in the above code, if there was a call to a database then it would automatically pick up the existing transaction.
// This transaction scope is similar to the COM+ Transaction.Required - it will inherit an existing transaction or create a new one if necessary
using (TransactionScope scope = new TransactionScope())
{
// The SqlConnection will pick up the existing ambient transaction
using (SqlConnection conn = new SqlConnection())
{
// ...
}
scope.Complete();
}
Longhorn: transactional registry and file system
Jim showed a demo of the transactional file system in Longhorn that used the same code as above but ensured that a file was only written to the file system if the transaction was successful. This will be a great development benefit, since doing this today requires that developers have to write their own compensating resource transaction. Currently it still has dependency on the DTC with a Kernel Ambient object, but this will be factored out by launch.
Overall I think that System.Transactions is an excellent addition to the .NET developers toolkit, since it makes it easy to guarantee that components will behave correctly even when faced with an error.