Skip to main content

Implicit Transaction using Transaction Scope

The TransactionScope class provides a simple way to mark a block of code as participating in a transaction, without requiring you to interact with the transaction itself. A transaction scope can select and manage the ambient transaction automatically. Due to its ease of use and efficiency, it is recommended that you use the TransactionScope class when developing a transaction application.
The following sample shows a simple usage of the TransactionScope class.
  • // This function takes arguments for 2 connection strings and commands to create a transaction
  • // involving two SQL Servers. It returns a value > 0 if the transaction is committed, 0 if the
  • // transaction is rolled back. To test this code, you can connect to two different databases
  • // on the same server by altering the connection string, or to another 3rd party RDBMS by
  • // altering the code in the connection2 code block.
  • static public int CreateTransactionScope(
  • string connectString1, string connectString2,
  • string commandText1, string commandText2)
  • {
  • // Initialize the return value to zero and create a StringWriter to display results.
  • int returnValue = 0;
  • System.IO.StringWriter writer = new System.IO.StringWriter();
  • try
  • {
  • // Create the TransactionScope to execute the commands, guaranteeing
  • // that both commands can commit or roll back as a single unit of work.
  • using (TransactionScope scope = new TransactionScope())
  • {
  • using (SqlConnection connection1 = new SqlConnection(connectString1))
  • {
  • // Opening the connection automatically enlists it in the
  • // TransactionScope as a lightweight transaction.
  • connection1.Open();
  • // Create the SqlCommand object and execute the first command.
  • SqlCommand command1 = new SqlCommand(commandText1, connection1);
  • returnValue = command1.ExecuteNonQuery();
  • writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
  • // If you get here, this means that command1 succeeded. By nesting
  • // the using block for connection2 inside that of connection1, you
  • // conserve server and network resources as connection2 is opened
  • // only when there is a chance that the transaction can commit.
  • using (SqlConnection connection2 = new SqlConnection(connectString2))
  • {
  • // The transaction is escalated to a full distributed
  • // transaction when connection2 is opened.
  • connection2.Open();
  • // Execute the second command in the second database.
  • returnValue = 0;
  • SqlCommand command2 = new SqlCommand(commandText2, connection2);
  • returnValue = command2.ExecuteNonQuery();
  • writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
  • }
  • }
  • // The Complete method commits the transaction. If an exception has been thrown,
  • // Complete is not called and the transaction is rolled back.
  • scope.Complete();
  • }
  • }
  • catch (TransactionAbortedException ex)
  • {
  • writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
  • }
  • catch (ApplicationException ex)
  • {
  • writer.WriteLine("ApplicationException Message: {0}", ex.Message);
  • }
  • // Display messages.
  • Console.WriteLine(writer.ToString());
  • return returnValue;
  • }
The transaction scope is started once you create a new TransactionScope object.  As illustrated in the code sample, it is recommended that you create scopes with a using statement. The using statement is available both in C# and in Visual Basic, and works like a try…finally block to ensure that the scope is disposed of properly.
When your application completes all the work it wants to perform in a transaction, you should call the Complete method only once to inform the transaction manager that it is acceptable to commit the transaction. It is very good practice to put the call to Complete as the last statement in the using block.
Failing to call this method aborts the transaction, because the transaction manager interprets this as a system failure, or equivalent to an exception thrown within the scope of transaction. However, calling this method does not guarantee that the transaction wil be committed. It is merely a way of informing the transaction manager of your status. After calling the Complete method, you can no longer access the ambient transaction by using the Current property, and attempting to do so will result in an exception being thrown.
If the TransactionScope object created the transaction initially, the actual work of committing the transaction by the transaction manager occurs after the last line of code in the using block.
The using statement ensures that the Dispose method of the TransactionScope object is called even if an exception occurs. The Dispose method marks the end of the transaction scope. Exceptions that occur after calling this method may not affect the transaction. This method also restores the ambient transaction to it previous state.
A TransactionAbortedException is thrown if the scope creates the transaction, and the transaction is aborted.System.Transactions.TransactionIndoubtException is thrown if the transaction manager cannot reach a Commit decision. No exception is thrown if the transaction is committed.
If you want to rollback a transaction, you should not call the Complete method within the transaction scope. For example, you can throw an exception within the scope. The transaction in which it participates in will be rolled back.
Transaction scope can be nested by calling a method that uses a TransactionScope from within a method that uses its own scope, as is the case with the RootMethod method in the following example
  • void RootMethod()
  • {
  • using(TransactionScope scope = new TransactionScope())
  • {
  • /* Perform transactional work here */
  • SomeMethod();
  • scope.Complete();
  • }
  • }
  • void SomeMethod()
  • {
  • using(TransactionScope scope = new TransactionScope())
  • {
  • /* Perform transactional work here */
  • scope.Complete();
  • }
  • }
The top-most transaction scope is referred to as the root scope. When a TransactionScope object joins an existing ambient transaction, disposing of the scope object may not end the transaction, unless the scope aborts the transaction. If the ambient transaction was created by a root scope, only when the root scope is disposed of, does Commit get called on the transaction. If the transaction was created manually, the transaction ends when it is either aborted, or committed by its creator.

Comments

Popular posts from this blog

gcAllowVeryLargeObjects Element

There are numerous new features coming with .NET 4.5 and here, on this blog, you can find several posts about it. But the feature we are goint to talk about today is very exciting, because we were waiting for it more than 10 years. Since .NET 1.0 the memory limit of .NET object is 2GB. This means you cannot for example create array which contains elements with more than 2GB in total. If try to create such array, you will get the OutOfMemoryException. Let’s see an example how to produce OutOfMemoryException. Before that Open Visual Studio 2012, and create C# Console Application, like picture below. First lets create simple struct with two double members like example below: 1 2 3 4 5 6 7 8 9 10 11 12 public struct ComplexNumber {      public double Re;      public double Im;      public ComplexNumber( double re, double im)      {          Re=re;          Im=im;      } } As we know this structure consumes about 16

Support for debugging lambda expressions with Visual Studio 2015

Anyone who uses LINQ (or lambdas in general) and the debugger will quickly discover the dreaded message “Expression cannot contain lambda expressions”. Lack of lambda support has been a limitation of the Visual Studio Debugger ever since Lambdas were added to C# and Visual Basic.  With visual studio 2015 Microsoft has added support for debugging lambda expressions. Let’s first look at an example, and then I’ll walk you through current limitations. Example To try this yourself, create a new C# Console app with this code: using System.Diagnostics; using System.Linq; class Program { static void Main() { float[] values = Enumerable.Range(0, 100).Select(i => (float)i / 10).ToArray(); Debugger.Break(); } } Then compile, start debugging, and add “values.Where(v => (int)v == 3).ToArray()” in the Watch window. You’ll be happy to see the same as what the screenshot above shows you. I am using Visual Studio 2015 Preview and it has some limitations.

How to allow a very large object in .net application?

Since .NET 1.0 the memory limit of .NET object is 2GB. This means you cannot for example create array which contains elements with more than 2GB in total. If try to create such array, you will get the OutOfMemoryException. Let’s see an example how to produce OutOfMemoryException. Before that Open Visual Studio, and create C# Console Application. Lets create simple struct with two double members like example below: public struct ComplexNumber { public double Re; public double Im; public ComplexNumber(double re, double im) { Re = re; Im = im; } } As we know this structure consumes about 16 bytes of memory. So if we want to create array of this type which consume more than 2GB we need to create array at least with 134217728 instances. So this sample program below creates 130000000 (about 1,97 GB) of array. int maxCount = 130000000; ComplexNumber[] arr = null; try { arr = new ComplexNumber[maxCount]; } catch (Exception ex) { Console.WriteLine(ex.Message); } So if we run t