TransactionScope in C#
Last Updated on
May 5, 2023
1. Introduction
Transaction management is extremely significant for any business to seamlessly operate their business application. It changes from version to version like in version 2 of the .NET framework it provides management of its own transaction components using TransactionScope class. TransactionScope is a class of System Namespace. It can also be termed as Transactions Namespace. The TransactionScope class supports transactions from code blocks and that is why it plays a key role in the .NET development framework. This class usually manages local as well as distributed transactions from our code. TransactionScope is very simple, robust and easy to implement and that is why it is very popular among .NET developers. In this blog, we will cover all the aspects of encircling the Transactionscope along with a code sample for .NET developers.
2. TransactionScope
You might be wondering what TransactionScope is, so it goes exactly like the name- Scope of the transaction. TransactionScope is a class that makes code block transactions after a certain time. TransactionScope provides an absolute programming model in which the infrastructure itself manages the transactions automatically. TransactionScope reduces the complexity of code and thus it is widely accepted and used in custom software development.
The TransactionScope makes the block of code as a part of a transaction without requesting c# developers to connect with the transaction itself. A TransactionScope is allowed to select and manage ambient transaction automatically. Due to its ease of access and efficiency, C# TransactionScope class is highly recommended in transactional applications.
3. Use of TransactionScope
Now when you create or use transactions using ADO.NET, you just have to commit or rollback on the transaction object. In case you have forgotten to commit or rollback the transaction, it may cause problems and there can be a loss of data or inconsistent data problems. Such problems can be solved by using TransactionScope.
In the process of using TransactionScope, there are no manual choices to save(commit) or to rollback transactions for C# developers. If there is an exception faced then the operations will be taken back automatically and the exception will be caught in the catch block.
Once you are done with the process, you can commit the TransactionScope. As soon as you commit, the complete will be called. So in case of power failure, system crash or hardware failure TransactionScope considers an exception occurs in the transaction and all the transactions within the TransactionScope block will be automatically rolled back.
TransactionScope can be used to maintain multiple databases as well as a single database with multiple connection strings.
You don’t have to close any database connection in between of work while you are working with TransactionScope.
Due to these all advantages and ease of access, the TransactionScope is popular while creating transactional applications.
4. Transaction Properties
In the process of transaction, there is a common methodology that is followed with an acronym of ACID. The full form of ACID is Atomic, consistent, Isolation and durability. Let’s know what each of these terms means individually.
A- Atomic
Using Automatic, all the ongoing transactions undergo a single operation. So in this case, if all the parts of the transaction succeed individually then the whole database will inhibit the change. In case when a single part of the transaction fails then the whole database will remain unmodified. There are multiple causes for why it fails to execute changes, causes such as rule violation, power or hardware failure, etc.
C-Consistent
In the transaction, one thing is constant and that does not change from start to end ‘transaction of data’. Thus, we cannot conclude that the data cannot be changed. It changes from one state to another depending on the rules of data integration. Like setting a primary key or checking null value constraints integrity in references and similar cases.
I-Isolation
In-between transactions are hidden from each other and so two transactions that are performed simultaneously do not affect each other and are serialized.
D-Durable
Once you complete the transaction and execute the commit then in any case the data loss will not occur. The data is durable even in case of system failure, crashing of the database, power cut, etc.
System.Transactions Namespace
System.Transactions allow you to create, enlist, and control your Transactional applications using classes that are provided in this namespace. The transaction was introduced in .Net framework version 2.0 and it is one of the vital design goals used to create and allow users to participate in transactions whether local or distributed within one or multiple participants.
Create TransactionScope
The object responsible for creating the TransactionScope class also creates the TransactionScope. The ‘Using’ type of sentence is used to initiate an object in the TransactionScope class. “Using” statement is used to check whether the scope is Disposed of appropriately or not. This option is available in both C# and Visual basics which is said to function similar to “Try-catch-finally”.
5. Complete and End a TransactionScope in C# Development
To complete the TransactionScope, theComplete method is used. In a transaction when all the tasks are completed by the application, users can Commit the transaction. This command can be called only once during the entire session by implementing the “Complete method ” to keep the underlying transaction manager informed about the successful transaction.
If this method is not called in the entire application, the transaction manager determines it as an exception thrown or system failure within the TransactionScope and it aborts the transaction. Whether the Dispose method of TransactionScope is called or not it is ensured by the “using” statement even if the exception occurs. Once you call off the “Dispose method” ,the occurring exception may not affect the transaction anymore. The dispose method also tries to restore the environmental changes by transaction and transforms it into its previous state.
When you don’t use TransactionScope, you will have to manage the complete transaction on your own. The code you develop knows how the transaction works.
private static void ExecuteSqlTransactionWithoutTransactionScope(string connectionString) { using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); SqlCommand sqlCommand = sqlConnection.CreateCommand(); SqlTransaction sqlTransaction; // Start a transaction. sqlTransaction = sqlConnection.BeginTransaction("MyTransaction"); sqlCommand.Connection = sqlConnection; sqlCommand.Transaction = sqlTransaction; try { sqlCommand.CommandText = "Insert into Employee(Name,Department) VALUES('Vishal Jain', 'Development')"; sqlCommand.ExecuteNonQuery(); sqlCommand.CommandText = "Insert into Employee(Name,Department) VALUES('Ronal Patel', 'QA')"; sqlCommand.ExecuteNonQuery(); // Attempt to commit the transaction. sqlTransaction.Commit(); Console.WriteLine("Both employees have been inserted in the database."); } catch (Exception ex) { // Log the exception try { //Rollback the transaction sqlTransaction.Rollback(); } catch (Exception exception) { // This catch block will handle any errors that may have occurred // on the server that would cause the rollback to fail. } } } } |
You can observe and compare the codes that use TransactionScope with the code above.
private static void ExecuteSqlTransactionWithTransactionScope(string connectionString) { using (TransactionScope transactionScope = new TransactionScope()) { using (SqlConnection sqlConnection = new SqlConnection(connectionString)) { sqlConnection.Open(); SqlCommand sqlCommand = sqlConnection.CreateCommand(); sqlCommand.Connection = sqlConnection; sqlCommand.CommandText = "Insert into Employee(Name,Department) VALUES('Vishal Jain', 'Development')"; sqlCommand.ExecuteNonQuery(); sqlCommand.CommandText = "Insert into Employee(Name,Department) VALUES('Ronal Patel', 'QA')"; sqlCommand.ExecuteNonQuery(); Console.WriteLine("Both employees have been inserted in the database."); } transactionScope.Complete(); } } |
transactionScope.Complete() clearly shows that all the operations inside the specific scope are completed without any failures.
There are multiple sources and links involved in TransactionScope which is shown below:
try { using (TransactionScope transactionScope = new TransactionScope()) { // Your first operation with SQL Connection String A using (connection = new SqlConnection(connectionStringA)) { connection.Open(); // Your first operation // Your second operation // ... } // Your second operation with SQL Connection String B using (connection = new SqlConnection(connectionStringB)) { connection.Open(); // Your first operation // Your second operation // ... } // ... // if success so far, commit the transaction transactionScope.Complete(); } } catch (Exception ex) { // transaction will be rolled back if exception occurs // ... } |
Once all your operations are completed, you can inform the transaction manager about the same. Transaction manager determines and commit the transaction and call it a complete method. If this method is not called then there is a possibility of aborting the transaction.
6. Nested Transaction
We can have nested transactions as well. It could be as below:
public void ParentTransaction() { try { using (TransactionScope transactionScope = new TransactionScope()) { using (connection = new SqlConnection(connectionString1)) { connection.Open(); // Parent Operation } ChildTransaction(); transactionScope.Complete(); } } catch (ThreadAbortException ex) { // Handle the exception } } private void ChildTransaction() { using (TransactionScope transactionScope = new TransactionScope()) { using (connection = new SqlConnection(connectionString2)) { connection.Open(); // Child Operation } transactionScope.Complete(); } } |
In this phase, we call the outermost transaction as Rootscope and innermost as ChildTransaction. This phenomenon is terminated by the transactionScope.Compelete() command. If the rootscope is terminated because of any reason then it will automatically rollback with inner transactions.
Note: You may face exceptions when you are executing separate transactions. Issues such as
- MSDTC become inaccessible on the server
- The access of MSDTC for distributed transactions is turned off.
Both these obstacles occur due to some reasons. Reasons such as the database and the application can be on the same server or it can occur because of running it on other servers. If you want to make it seamless then go to run => services.mnc and run the service named Distributed Transaction and automate the start type so that if the system needs to restart it can. Then follow the next step to configure MSDTC.
7. TransactionScope Default Properties in C# Development
TransactionScope has three very important default Properties, let’s discuss each one in brief.
- Isolation Level
- Timeout
- TransactionScope Option
1. Isolation Level
The Isolation Level of a transaction that specifies the level of access other transactions have to volatile data before the transaction is completed. You can indicate the isolation level that applies to the transaction while creating the transaction. Normally, when you execute a transaction, its isolation level is set to a flexible level to serialize. Other isolation levels are commonly utilized for read-intensive systems. The way of using read and write locks is different at different isolation levels.
DefaultValue: Serializable
AvailableOptions: Serializable, Read Committed, Read Uncommitted, Repeatable Read
2. Timeout
Timeout property is used to control the timeout of the transaction. C# developers can set infinite timeout by setting timeout zero. In case when you are debugging, the infinite timeout is extremely useful.
DefaultValue: 1 minute
AvailableOptions: Maximum 10 Minutes
3. TransactionScope Option
TransactionScopeOption is an enumeration which provides additional options for creating a TransactionScope. TransactionScope has three TransactionScopeOption.
Required: If the scope is instantiated with the ‘Required’ option and if an ambient transaction is present then the scope joins the transaction and if there is no ambient transaction present then the new transaction will be created by the scope and become the root scope.
RequiresNew: The scope will always become a root scope if it is instantiated with ‘RequiresNew‘. The transaction will act differently and turn to become a new ambient transaction when it is in a new environment.
Suppress: The scope never takes part in a transaction, if it is instantiated with ‘Suppress’. Ambient transactions for TransactionScope with this value always have null.
DefaultValue: Required
AvailableOptions: Required, RequiresNew, Suppress
Please see below example:
using (TransactionScope scope = new TransactionScope()) { using (TransactionScope scope1 = new TransactionScope(TransactionScopeOption.Required)) { // Do Operation scope1.Complete(); } using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.RequiresNew)) { // Do Operation scope2.Complete(); } using (TransactionScope scope3 = new TransactionScope(TransactionScopeOption.Suppress)) { // Do Operation scope3.Complete(); } scope.Complete(); } |
Now, we have created parent transactions and under this, there are three transactions with different TransactionScope Options. Here, You can imply the scope in the parent section of the transactions. The rootscope transaction creates a new transaction and then later it will consider this as an ambient transaction. The ambient transaction is the transaction within which your code executes.
We create Scope1(already an ambient transaction) as and when needed which is later conjoined with the parent transaction. Then later, when new requirements are added we need to create an option of RequiresNew that is programmed to work independently with ambient transactions.
Using the Suppress option, create Scope 3 which will not take part in any ambient process. Do not wait for the success or failure of the ambient process, you can initiate the process immediately after using the suppress option.
8. TransactionScope and Async/Await
In the recent version 4.5.0 of the .NET framework, there is a critical bug regarding the System.Transactions.Transaction scope does not allow a smooth transaction when the function collides with async /await. This bug interrupts the flow of Transaction Scope in the asynchronous flow cycle.
This has the capacity of changing the threading context of the transaction with bundles of exceptions thrown while disposing of the transaction scope. This is a brainstorming challenge because when we write code in the asynchronous method it should be bug-free
The best part here is that .NET framework 4.5.1 is “Asynchronous continuation”. This is a quick trick released by Microsoft to fix the bug from the previous version. They provide a new opt-in facility for developers to fix the error. Enlisted below are some guidelines on how to use it.
- Firstly you need to upgrade your version .NET 4.5.1 right away so that you can sync the transaction scope and async/await together.
- When you cover the TransactionScope with a code then you need to specify it with a constructor like TransactionScopeAsyncFlowOption.Enabled
9. Why Not Using .NET Transactions Along with EntityFramework
The default isolation model is a perfect fit for the majority of your needs. Whether it is to read data or overwrite or if you want to modify the database with commands like Create, Update, Delete EntityFramework. This is one of the thriving methods used to create transactions without wrapping changes processed in the background. Since it is atomic,without the explicit use of transactions. everything you save will be integrated into the system without any discarding of data. Because of this it is good not to use .NET transactions along with entity framework.
When you use EntityFramework, you have the potency to change how it behaves in the framework. We need to enforce the CRUD function to be executed in a serialized fashion and isolation mode within the transaction scope. This is secure which does not allow users to access tables during the transaction. This rapidly creates a deadlock situation which is unavoidable at any cost.
This is how the code looks without the explicit use of transactions.
// DBC = Database Command // create the database context var dbContext = new DatabaseContext(); // search for the employee with id #100 // DBC: select * from [Employee] where Id = 100 var employeeA = dbContext.Employees.Find(100); // search for the employee with id #200 // DBC: select * from [Employee] where Id = 200 var employeeB = dbContext.Employees.Find(200); employeeA.Name = "Ronak Patel"; employeeB.Department = "QA"; // DBC: BEGIN TRANSACTION // DBC: update Employee set Name = 'Ronak Patel' where Id = 100 // DBC: update Employee set Department = 'QA' where Id = 200 // DBC: COMMIT TRANSACTION dbContext.SaveChanges(); |
10. Conclusion
Our intent to create this blog was to shed light on the creation and use of Transaction and TransactionScope. It provides the best way to handle transactions from the code side. It is easy to use and simpler than other transaction handling methods. Using TransactionScope, you can handle the transaction with less code as you don’t need to write multiple statements which lead to increased code readability and efficiency.
View Comments
Great article about using TransactionScope in C#! This article provides a complete overview of transactions in C# and gives a step-by-step guide to managing transactions in C#. I especially like nested transactions. Also give me a better understanding of the uses of TransactionScope, its properties in C# development, and much more.
I had some basic knowledge about working of transaction scope in c# but I lacked in information about the properties of transaction which is called by an acronym of ACID. Indeed it was very helpful to know about the bug of the .NET framework of version 4.5.0 it will be helpful in future when I will be working with async /await. The default properties of a transaction in c# are mentioned in easy language overall it was an awesome read, would definitely recommend it to others.
Indeed I agree that Transaction Scope is the simplest way to handle all the transactions in c#. All thanks to .NET Framework version 2.0 for introduction Transaction Scope. It helps in increasing code readability and efficiency by allowing developers to write database code and logical code in separate functions or classes. Which then later can be called directly in transactions. This blog is a great read. I found default properties of transaction scope in c# very helpful!
I agree that handling transactions are critical for any business application. Indeed TransactionScope is an essential class in the .NET Framework which supports transactions from code blocks. The How to use section is very helpful and I can use these references for my future .NET Development projects. Additionally, the list of properties of TransactionScope for C# Development is explained in a very simple and effective way.