An older version of re-store required the programmer to discard the client transaction explicitly, thereby handing over the transaction and its domain object instances to the garbage collector. This is what the API for discarding a client transaction looks like:
myClientTransaction.Discard (); // discards my client transaction |
Note that we have not used this method in the PhoneBook.Sample
. Simple! However, the explicit use of the Discard()
is strongly discouraged. There is a better, more modern way. In the old days (until 2007), if the programmer forgot to discard client transactions, they were piling up, clogging resources. This sensitivity to absent minds resembles memory leaks in languages without garbage collection. (malloc()
without free()
in C, for example, or new
without delete
in C++).
A transaction scope is designed to look and feel like the scope of a method in C#. The scope of local variables of the method is limited to the space between the curly braces:
Transaction scopes work in a similar fashion. The programmer puts up a transaction scope when creating the client transaction. You have seen this already in the section with the PhoneBook report generators (PhoneBook.Sample, Enhancing and using the PhoneBook.Domain
library). Remember the construction
using(ClientTransaction.CreateRootTransaction().EnterDiscardingScope()) { // lots of domain object code here ClientTransaction.Current.Commit(); } |
This scope limits the life of the scoped transaction to the space between the curly braces. When the scope is left, the client transaction is discarded automatically, just like a local variable is removed from the call stack when the function (or method) is left. The most interesting detail here is that ClientTransaction.Current
always holds the client transaction instance that has been created for the scope ClientTransaction.Current
is in. The using and its delimiting braces limit the scope of the transaction created for the using:
The call chain
ClientTransaction.CreateRootTransaction().EnterDiscardingScope()
is the most typical way of creating transactions and a corresponding transaction scope:
ClientTransaction.CreateRootTransaction()
creates a new client (root) transaction (just as the name suggests). It is a static method belonging to the class ClientTransaction
.EnterDiscardingScope()
.A discarding scope is a transaction scope that behaves as explained here: it makes the transaction that belongs to the scope vanish without a trace as soon as execution of the program leaves the scope. (The alternative is FIXME.)
Note how easy it is to answer the following questions for the illustrated listing above:
ClientTransaction.Current
? Answer: the one created for the braces delimiting the transaction scope.As explained in the section FIXME above, root transactions are not all there is to it. Sub-transactions are created with the method CreateSubTransaction
. This method is not static, however, it is a member of the given client transaction instance. For spawning a sub-transaction from a given client transaction, just call its CreateSubTransaction()
method. Call EnterDiscardingScope()
like you do for a root transaction.
This works for arbitrarily nested transactions and sub-transactions. Here is a more complicated illustration:
ClientTransaction.Current
in the outer scope references contains a reference to the corresponding root creation for which is has been created.ClientTransaction.Current
. ClientTransaction.Current
holds a reference to that sub-transaction for which it has been created. As soon as execution leaves the inner scope, the sub-transaction is forgotten and falls prey to .NET's garbage collector.ClientTransaction.Current
is restored so that it references the outer scope's root transaction again.Sub-transactions can be spawned and nested to arbitrary depth:
using (ClientTransaction.CreateRootTransaction().EnterDiscardingScope()) { using (ClientTransaction.Current.CreateSubTransaction().EnterDiscardingScope()) { using (ClientTransaction.Current.CreateSubTransaction().EnterDiscardingScope()) { using (ClientTransaction.Current.CreateSubTransaction().EnterDiscardingScope()) { using (ClientTransaction.Current.CreateSubTransaction().EnterDiscardingScope()) { ... more, if you want... } } } } } |