[ Team LiB ] |
Recipe 6.3 Nesting Manual Transactions with the SQL Server .NET Data ProviderProblemYou need to create a nested transaction using the SQL Server .NET data provider, but the Begin( ) command that you need is only available with the OLE DB .NET data provider. The SQL Server data provider appears to provide no built-in support for nested transactions. You want to nest transactions when using it. SolutionSimulate nested transactions with savepoints when using the SQL Server .NET data provider, manage and control the lifetime of the SqlTransaction class, and create the required exception handling. The sample code contains two event handlers:
The C# code is shown in Example 6-4. Example 6-4. File: NestedManualTransactionForm.cs// Namespaces, variables, and constants using System; using System.Configuration; using System.Windows.Forms; using System.Data; using System.Data.SqlClient; private const String CATEGORIES_TABLE = "Categories"; private DataTable dt; private SqlDataAdapter da; // . . . private void NestedTransactionForm_Load(object sender, System.EventArgs e) { // Fill the categories table. String sqlText = "SELECT CategoryID, CategoryName, " + "Description FROM Categories"; da = new SqlDataAdapter(sqlText, ConfigurationSettings.AppSettings["Sql_ConnectString"]); dt = new DataTable(CATEGORIES_TABLE); da.FillSchema(dt, SchemaType.Source); da.Fill(dt); // Bind the default view of the table to the grid. dataGrid.DataSource = dt.DefaultView; } private void insertButton_Click(object sender, System.EventArgs e) { String sqlText = "INSERT " + CATEGORIES_TABLE + " "+ "(CategoryName, Description) VALUES " + "(@CategoryName, @Description)"; // Create the connection. SqlConnection conn = new SqlConnection( ConfigurationSettings.AppSettings["Sql_ConnectString"]); // Create the transaction. conn.Open( ); SqlTransaction tran = conn.BeginTransaction( ); // Create command in the transaction with parameters. SqlCommand cmd = new SqlCommand(sqlText, conn, tran); cmd.Parameters.Add(new SqlParameter("@CategoryName", SqlDbType.NVarChar, 15)); cmd.Parameters.Add(new SqlParameter("@Description", SqlDbType.NVarChar, 100)); try { // Insert the records into the table. if (categoryName1TextBox.Text.Trim( ).Length == 0) // If CategoryName is empty, make it null (invalid). cmd.Parameters["@CategoryName"].Value = DBNull.Value; else cmd.Parameters["@CategoryName"].Value = categoryName1TextBox.Text; cmd.Parameters["@Description"].Value = description1TextBox.Text; cmd.ExecuteNonQuery( ); } catch (Exception ex) { // Exception occurred. Roll back the transaction. tran.Rollback( ); MessageBox.Show(ex.Message + Environment.NewLine + "Transaction rollback (records 1 and 2)."); conn.Close( ); return; } tran.Save("SavePoint1"); try { // Insert the records into the table. if (categoryName2TextBox.Text.Trim( ).Length == 0) // If CategoryName is empty, make it null (invalid). cmd.Parameters["@CategoryName"].Value = DBNull.Value; else cmd.Parameters["@CategoryName"].Value = categoryName2TextBox.Text; cmd.Parameters["@Description"].Value = description2TextBox.Text; cmd.ExecuteNonQuery( ); // If okay to here, commit the transaction. tran.Commit( ); MessageBox.Show("Transaction committed (records 1 and 2)."); } catch (SqlException ex) { tran.Rollback("SavePoint1"); tran.Commit( ); MessageBox.Show(ex.Message + Environment.NewLine + "Transaction commit (record 1)." + Environment.NewLine + "Transaction rollback (record 2)."); } finally { conn.Close( ); } // Refresh the data. da.Fill(dt); } DiscussionThe OLE DB .NET data provider's transaction class OleDbTransaction has a Begin( ) method that is used to initiate a nested transaction. A nested transaction allows part of a transaction to be rolled back without rolling back the entire transaction. An InvalidOperationException is raised if the OLE DB data source does not support nested transactions. The SQL Server .NET data provider's transaction class SqlTransaction does not have a Begin( ) method to initiate a nested transaction. Instead, it has a Save( ) method that creates a savepoint in the transaction that can later be used to roll back a portion of the transaction—to the savepoint rather than rolling back to the start of the transaction. The savepoint is named using the only argument of the Save( ) method. An overload of the Rollback( ) method of the SqlTransaction class accepts an argument that you can use to specify the name of the savepoint to roll back to. |
[ Team LiB ] |