The Transaction
class
from the System.Transactions
namespace, introduced in .NET 2.0, represents the transaction that all
.NET transaction managers work with:
[Serializable]
public class Transaction : IDisposable,ISerializable
{
public static Transaction Current
{get;set;}
public void Rollback(); //Abort the transaction
public void Dispose();
//More members
}
Developers rarely need to interact with the Transaction
class directly. The main use of
the Transaction
class is to manually
abort a transaction by calling the Rollback()
method.
Additional features of the Transaction
class include enlisting resource
managers, setting the isolation level, subscribing to transaction
events, cloning the transaction for concurrent threads, and obtaining
the transaction status and other information.
.NET 2.0 defined a concept called the ambient
transaction, which is the transaction in which your code
executes. To obtain a reference to the ambient transaction, call the
static Current
property of
Transaction
:
Transaction ambientTransaction = Transaction.Current;
If there is no ambient transaction, Current
will return null
. Every piece of code, be it client or
service, can always reach out for its ambient transaction. The ambient
transaction object is stored in the thread local storage (TLS). As a result, when the thread
winds its way across multiple objects and methods on the same call
chain, all objects and methods can access their ambient
transactions.
In the context of WCF, the ambient transaction is paramount. When present, any resource manager will automatically enlist in the ambient transaction. When a client calls a WCF service, if the client has an ambient transaction and the binding and the contract are configured to allow transaction flow, the ambient transaction will propagate to the service.
The client cannot propagate an already aborted transaction to the service. Doing so will yield an exception.
The Transaction
class
is used both for local and distributed transactions. Each transaction
object has two identifiers used to identify the local and the
distributed transaction. You obtain the transaction identifiers by
accessing the TransactionInformation
property of the
Transaction
class:
[Serializable] public class Transaction : IDisposable,ISerializable { public TransactionInformation TransactionInformation {get;} //More members }
The TransactionInformation
property is of the type TransactionInformation
, defined as:
public class TransactionInformation { public Guid DistributedIdentifier {get;} public string LocalIdentifier {get;} //More members }
TransactionInformation
offers
access to the two identifiers. The main use of these identifiers is
for logging, tracing, and analysis. In this chapter, I will use the
identifiers as a convenient way to demonstrate transaction flow in
code as a result of configuration.
The local transaction identifier
(local ID) contains both an identifier for the LTM in the current
app domain as well as an ordinal number enumerating the transaction.
You access the local ID via the LocalIdentifier
property of TransactionInformation
. The local ID is
always available with the ambient transaction, and as such is never
null
: as long as there is an
ambient transaction, it will have a valid local ID.
The value of the local ID has two parts: a constant GUID that is unique for each app domain and represents the assigned LTM for that app domain, and an incremented integer enumerating the transactions managed so far by that LTM.
For example, if a service traces three consecutive transactions, starting with the first call, it will get something like this:
8947aec9-1fac-42bb-8de7-60df836e00d6:1
8947aec9-1fac-42bb-8de7-60df836e00d6:2
8947aec9-1fac-42bb-8de7-60df836e00d6:3
The GUID is constant per app domain. If the service is hosted in the same app domain as the client, they will have the same GUID. If the client makes a cross-app domain call, the client will have its own unique GUID identifying its own local LTM.
The distributed transaction
identifier (distributed ID) is generated automatically
whenever an LTM- or KTM-managed transaction is promoted to a DTC-managed
transaction (for example, when the ambient transaction flows to
another service). You access the distributed ID via the DistributedIdentifier
property of
TransactionInformation
. The distributed ID
is unique per transaction, and no two transactions will ever have
the same distributed ID. Most importantly, the distributed ID will
be uniform across the service boundaries and across the entire call
chain, from the topmost client through every service and object down
the call chain. As such, it is useful in logging and tracing. Note
that for a transaction that has not yet been promoted, the value of
the distributed ID will be Guid.Empty
. The distributed ID is usually
Guid.Empty
on the client side
when the client is the root of the transaction and it has not yet
called a service, and on the service side it will be empty if the
service does not use the client’s transaction and instead starts its
own local transaction.
3.22.27.45