C# > Data Access > Database Access > Transactions in ADO.NET

Basic ADO.NET Transaction

This code snippet demonstrates a basic transaction using ADO.NET. It shows how to wrap multiple database operations within a transaction to ensure atomicity. Either all operations succeed, or none do. This snippet uses a SqlConnection, SqlCommand, and SqlTransaction objects to perform the transaction.

Code Snippet

This code establishes a connection to a SQL Server database using the provided connection string. A new transaction is initiated. Two SQL commands are executed within the transaction: one inserts a new product, and the other updates the price of an existing product. If both commands execute successfully, the transaction is committed. If any exception occurs, the transaction is rolled back to undo any changes made.

using System; 
using System.Data.SqlClient;

public class TransactionExample
{
    public static void ExecuteTransaction(string connectionString)
    {
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            connection.Open();

            // Start a local transaction.
            SqlTransaction transaction = connection.BeginTransaction();

            // Must assign both transaction object and connection
            // to Command object for a pending local transaction
            // to be rolled back on failure.
            try
            {
                SqlCommand command = new SqlCommand("INSERT INTO Products (ProductName, Price) VALUES ('NewProduct1', 20.00)", connection, transaction);
                command.ExecuteNonQuery();

                command = new SqlCommand("UPDATE Products SET Price = Price * 1.1 WHERE ProductName = 'ExistingProduct'", connection, transaction);
                command.ExecuteNonQuery();

                // Attempt to commit the transaction.
                transaction.Commit();
                Console.WriteLine("Transaction committed.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Transaction failed: " + ex.Message);

                // Attempt to roll back the transaction.
                try
                {
                    transaction.Rollback();
                    Console.WriteLine("Transaction rolled back.");
                }
                catch (Exception exRollback)
                {
                    // Throws if the transaction has already been closed or is
                    // invalid.
                    Console.WriteLine("Rollback failed: " + exRollback.Message);
                }
            }
        }
    }
}

Concepts Behind the Snippet

Atomicity: Transactions ensure that database operations are treated as a single, indivisible unit. This means either all operations within the transaction succeed, or none of them do. This guarantees data consistency. Consistency: Transactions maintain the integrity of the database by ensuring that data remains valid throughout the transaction. Isolation: Transactions are isolated from each other, meaning that one transaction cannot interfere with another. Durability: Once a transaction is committed, the changes are permanent and will survive even system failures.

Real-Life Use Case

Consider an e-commerce website where a user places an order. The order involves multiple operations, such as deducting the item's price from the user's account balance, creating an order record in the database, and updating the product's inventory. Wrapping these operations in a transaction ensures that either all operations succeed (order is placed correctly), or none of them do (user's balance isn't deducted if inventory update fails, preventing inconsistent data).

Best Practices

Keep Transactions Short: Long-running transactions can lock resources and impact performance. Try to keep transactions as short as possible. Handle Exceptions Properly: Always handle exceptions within the transaction and attempt to roll back if any error occurs. Use 'using' Statements: Ensure that SqlConnection and SqlCommand objects are disposed of correctly using 'using' statements to avoid resource leaks. Connection String Security: Store connection strings securely, avoid hardcoding them directly in the code.

Interview Tip

When asked about transactions, emphasize the ACID properties and explain how they ensure data integrity. Also, be prepared to discuss potential issues like deadlocks and how to prevent them.

When to Use Them

Use transactions whenever you need to perform multiple database operations that must be treated as a single, atomic unit. This is critical when maintaining data consistency and integrity is paramount.

Memory Footprint

The memory footprint of a transaction is relatively small, mainly consisting of the SqlConnection, SqlCommand, and SqlTransaction objects. However, long-running transactions that hold locks on many resources can indirectly impact memory usage due to caching and other database-related activities.

Alternatives

Entity Framework Core Transactions: Offers a higher-level abstraction for managing transactions, simplifying the code. Distributed Transactions (MSDTC): Used when transactions span multiple databases or resource managers. Considered more complex and can impact performance. Compensating Transactions (Sagas): Used in microservices architectures to handle eventual consistency when a single transaction spanning multiple services is not feasible.

Pros

Data Integrity: Ensures data consistency by treating multiple operations as a single atomic unit. Error Handling: Simplifies error handling by allowing you to roll back changes if any operation fails.

Cons

Performance Overhead: Transactions can introduce some performance overhead due to locking and logging. Complexity: Implementing transactions can add complexity to the code, especially when dealing with nested or distributed transactions. Deadlocks: Improperly designed transactions can lead to deadlocks, where two or more transactions are blocked indefinitely, waiting for each other to release resources.

FAQ

  • What happens if I don't commit or rollback a transaction?

    If you don't explicitly commit or rollback a transaction, the resources held by the transaction will remain locked. The database system will eventually rollback the transaction (often after a timeout period), but this can lead to performance issues and potential blocking of other operations.
  • How do I handle nested transactions?

    ADO.NET doesn't directly support nested transactions in the same way that some other database systems do. However, you can simulate nested transactions using savepoints. A savepoint marks a point within a transaction to which you can roll back if a later operation fails.