System.Transactions: Let’s get transactional!
June 10, 2006
This past week I was in Sydney teaching our Industrial Strength .NET course. One of the topics that we covered yesterday was the various transaction control mechanisms available to .NET programmers and the patterns that you can use to implement them into your code. Our current IS.NET material is based on .NET 1.1 but one of the things that I can do as an instructor is splice in some .NET 2.0 material.
Following on from the discussion about “Services without Components”, I decided to quickly show how the classes and interfaces in the new System.Transactions namespace work together to provide a standard way to enlist in a distributed transaction in managed code.
TransactionScope
One of the central classes in the System.Transactions namespace from an application developers point of view is the TransactionScope class. The transaction scope class when created puts the code that follows within the scope of a logical transaction. The canonical example is creating a transaction scope and then going and doing something nasty to a database.
In this case I am connecting to the database and executing an unqualified delete command against one of the tables which would result in all the rows being removed. Fortunately because I am inside a transaction scope and I didn’t explicitly say that the transaction is complete it is assumed when I leave the scope of the outer using construct that a rollback should be performed. The TransactionScope class only exposes a Complete and Dispose methods (and a number of contructor overloads). If the Dispose method is called prior to the Complete method being called it is assumed that a call back
If the dispose method is called prior to the Complete method being called it is assumed that all transactional operations performed within that scope should be rolled back. If I actually wanted to commit the transaction above I would have needed to execute the following code (don’t try this at home kids).
One of the things that you will notice is that I did nothing to explicitly enlist this command in a transaction. Thats because ADO.NET is aware of the functionality provided by the System.Transactions namespace and somewhere in its implementation it enlists a resource manager to respond to notifications from the transaction coordinator.
Building Your Own Resource Manager
One of the great things about the new System.Transactions namespace is that you can write your code to interface with the transaction coordinator and in that way make your code aware of the transaction and be able to respond to commit and rollback operations.
Your code can determine whether it is inside the scope of a transaction by grabbing the static Current property of the Transaction class a determining whether it is null or not.
The Current property on the transaction class holds a reference to a Transaction derived class which you can then use to enlist the resources under your control in the transaction. The Transaction class exposes a number of Enlist methods to support this and also exposes a traditional Rollback method.
Lets look at what it takes to build your own resource manager. The example that I am going to use is a simple calculator with Add(…), Subtract(…), Multiply(…) and Divide(…) functions. In the code below I’ve using it both inside and outside the scope of a transaction.
If the calculator is indeed behaving transactionally the output of the WriteLine(…) call would be “2.0”, if not, it will be “5.0”. We’ll start with a calculator implementation which is not transactional.
In order to enlist in a transaction we need to write a resource manager. A resource manager is a piece of code which records what operations are performed on the resources that it manages and provides a mechanism for rolling back to the state prior to the transaction starting. From a System.Transactions point of view the only requirement for the resource manager is that it implements IEnlistmentNotification.
The problem is, we’ve got a bit of a chicken and egg situation here. The resource manager is kind of useless unless we have some kind of transaction logging mechanism to keep track of what we are doing, and the transaction, but we can’t really build the log unless we can talk to the resource manager to use it.
Unfortunately this means that I’m going to have to do a bit of an object model dump here and then try and explain it all in one go.
In the model above we have a class called CalculatorContext. This is the resource manager for our transactional calculator (notice that it implements the IEnlistmentNotification interface). The context maintains the current state of the accumulator within the scope of the transaction and has a stack of commands that have been previously applied to that accumulator. The stack is fed by the ApplyCommand method which accepts any object which is derived from CalculatorCommand.
The CalculatorCommand class defines two abstract methods, Execute and Undo, both of which take an instance of the CalculatorContext. In order to hook into all of this infrastructure the Calculator class needs to get some treatment.
Notice how the methods now simply dispatch commands to the CalculatorContext instance “m_Context”, and how the Accumulator property just defers to the context to get the current value. The implementation of ApplyCommand is pretty straight forward, it first checks to see if we are running inside a transaction, and if thats the case whether its the first operation (by checking to see whether the command stack is initialised).
If its the first operation then it automatically enlists itself in the transaction, then pushes the command onto the command stack and executes it. On subsequent invocations in the same transaction it will just queue and execute commands. If there is no governing transaction then the command is just executed and no command history is maintained.
The only functionality left to implement on the resource manager is the actual Commit and Rollback implementations. Because we are keeping a rich history of what was done to the accumulator, all that needs to be done is that the stack needs to be cleared and set to null (in the case of a commit), or, in the case of a rollback the Undo methods need to be executed on each of the commands in history (then have the stack cleared and set to null).
Executing the code results in the expected output. However, in the interests of full disclosure I need to tell you about an interesting side effect of my implementation.
If I had not run the code that was inside the scope of the transaction then the output would have been “2”, not “2.0”. This is because the decimal type only outputs the decimal point when a floating point operation has been performed. So in reality my implementation is flawed if you consider that to be relevant. The way around that would have been for each command to store the previous value of the accumulator and just restore that rather than reversing out their operations, and as a matter of fact you could implement a fairly tight rollback implementation with no commands at all – but I like this approach.
Dealing with Isolation Levels
The code above does not deal with varying isolation levels. If two threads were accessing this code then the results could be highly random, or interesting at best. The System.Transactions namespace does support the specification of isolation levels but that is beyond the scope of this post.
As a suggestion though for how you can handle isolation levels, you could maintain a dictionary of seperate transactions and their resource managers inside the Calculator class. This means that each thread would return a different accumulator value depending on what it was set to inside the transaction. Unfortunately, because there is only one real accumulator on the calculator it would be a case of “last-commit-wins” or locking on pretty much the first operation.
One approach for understanding whether something needs to be locked is having each transaction communicate with the other transactions to determine whether any of their logged commands overlap in their resource usages, but once again thats outside the scope of this post, and would be pretty meaningless in this simple calculator example.
What sessions do I want to catch at TechEd 2006?
June 10, 2006
It looks like I am going to be catching TechEd 2006 in both Australia and New Zealand this year which means I need to carefully plan what I want to see. Michael Kleef has posted up the nearly complete session list and I decided that I would start building my not-so-short-list of what I am planning on catching. So here it is:
Architecture
- What do architects do anyway?
- Patterns and Anti-Patterns for Service-Oriented Architectures
- Architecting Identity – The identity metasystem and Infocard
- How to Get Your Grandmother Building Missile Defense Systems
Database and BI
- Corporate Performance Management on the Microsoft Business
- Intelligence Platform
- An Inside Look At an Exciting New Addition to Visual Studio Team System (Part 1)
Windows Client
- Developing Interactive Applications Using Windows Live Robots, Activities, and Alerts
- (WinFX) Introduction to InfoCard
Developer Tools
- Visual Studio 2005 Team Foundation Server – Applying Version Control, Work Item Tracking and Team Build to Your Software Development Project
- Visual Studio 2005 Team Foundation Server: Step-by-Step Migration and Adoption Planning
- VSTS Performance Tools
- Extending Team Foundation Server
- Programming with Concurrency in .NET: Concepts, Patterns, and Best Practices.
- Concurrent Development with Branching in Team Foundation Server
- Using Web Services to Develop Applications for Microsoft Dynamics CRM
Windows Server and Management
- PowerShell: Next Generation Command Line Scripting
Messaging and Mobility
- Exchange 2007 and the 2007 Microsoft Office System: Better Together
- Mail That Speaks to You: Unified Messaging in Exchange 2007
Exchange 2007 Web Services: 42 APIs is Not the Answer
Office System
- Infrastructure Topics in SharePoint Products and Technologies: Administrative Architecture and Planning for Deployment
- Microsoft Office SharePoint Designer 2007: Create and Customise SharePoint Web Sites and Build Workflow-Enabled Applications
- Document and Records Management Using 2007 Microsoft Office Server and Client Technologies
- Developing Workflows for the 2007 Microsoft Office System and Windows SharePoint Services (version 3)
- Microsoft Office Open XML Formats and Office Client Extensibility (COMBINED)
- Enterprise Search Technical Drilldown in Microsoft Office SharePoint Server 2007
- Integrating Microsoft Office InfoPath 2007 Forms into Workflow Solutions and Business Processes
- Microsoft Office Groove 2007: Enterprise Deployments
Security
- Attacker trends and techniques: an update
- Is That Application Really Safe?
Web Developer
- Developing Data-Driven Web Applications with .NET Language Integrated Query
- The Windows Live Platform: Build Applications That Have Access to 400 Million Address Books, and 13 Billion Contacts
- Windows Live Search Macros – Build, Share, and Use Your Own Search Engine in Seconds
- Introducing Microsoft Tools for Professional Designers: An Overview of Microsoft Expression
- Extending Your Reach with Microsoft Gadgets
- Expression Web Designer Overview
If I’m not going to your session and you think I should – post a comment and tell me why? This list obviously needs to be trimmed further.
Darren on Google Spreadsheets
June 10, 2006
Like lots of folks who actually have to work for a living Darren is picking up on the “big” news this week of Google releasing an online spreadsheet application. Like Darren I’m a bit unsure about how Google is really going to go after business here, but maybe thats not the intention.
If you remember your history Microsoft started out life targeting software at home users. This is the kind of market that Google is going to be going after with offerings like their Spreadsheets and Writely. I guess time will tell whether history will repeat itself and the home user market becomes the big business market.
I doubt that Microsoft is underestimating Google (anymore). But I also think that Microsofts latest operating system and productivity suite offering is going to simply outflank Google. The question is – will users buy it?
.NET Framework 3.0 eh?
June 10, 2006
So the news is out that Microsoft is going to roll the .NET Framework 2.0 and WinFX together and call them .NET Framework 3.0. From a technical standpoint I think it makes sense, and even from a marketing perspective there is goodness there, however I can see this causing problems for some early adopters.
Because upgrading from one framework version to another is considered to be such a major jump people may find it harder to adopt the new technologies in .NET Framework 3.0 not for any technical reason, but because the business is going to push back on jumping to a new framework version.
Now even though us geeks know that we are really just talking about the .NET Framework 2.0 plus some extra platform bits the decision makers in the enterprise space may not pick up on that subtle point.
In the long term however I think that this will be a good thing provided:
- Microsoft releases framework drops (major and minor) on a rolling basis (no more three year cycles).
- The upgrade path is maintained.
If they fail to do this then they won’t be able to train customers into upgrading to the newer frameworks as them come out and customers will resist because the cost of upgrading is too high.
Sometimes I think the tail should wag the dog to a certain extent when it comes to this stuff though.
MSDN Wiki
June 10, 2006
Frank Arrigo has posted up that MSDN now has a Wiki. You can visit the Wiki yourself, and check out my first contribution! I think that this kind of feature really increases the value of documentation because all the relevant discussion of a particular type can hang off it.
It would be really cool if they could start to aggregate other information sources like blogs and Google/MSNSearch search results and slot them in there as well.
Tax of broadband? Give me a break.
June 10, 2006
Juha has posted up with a link to an article in the Swedish press (sorry, no translation but you can probably get the gist of it). Basically, someone is entertaining the notion that we should tax broadband with proceeds going to copyright holders. The discussion follows a raid of of a torrent tracker on piracy claims.
The idea of introducing a tax on the entire broadband user population to pay for the actions of a subset of the population who are doing illegal things seems somewhat crazy to me. But beyond that there would be serious administration questions.
- How is it decided who gets a slice of the revenue?
- How do you ensure that content providers don’t slap a premium above and beyond the tax?
What it really comes down to is that I don’t really trust the government to spend my entertainment dollars for me, thats why they are my entertainment dollars. I am willing on the other hand to pay for my digital content if I agree with distribution mechanism.
I’ve all but given up on buying CDs because the risk of it not playing in my laptop is too high, and some of the rights management stuff on downloadable formats is a bit dodgy if you ask me.