TdCommandBuilder class, part of the .NET Data Provider for Teradata, generates Insert, Update and Delete commands for a Batch Update. But How can you associate a Transaction with CUD commands given that you have no direct control over the Command generation?
We need to start by looking at the basic relationship between TdCommandBuilder class and TdDataAdapter class. TdCommandBuilder has a DataAdapter property and it must be initialized. An Application must:
- Set the TdCommandBuilder.DataAdapter property.
- Set the TdDataAdaper.SelectCommand property of the associated TdDataAdapter object (TdCommandBuilder.DataAdapter).
- Set the TdCommand.Connection property of the associated TdCommand object (TdDataAdatper.SelectCommand).
The .NET Data Provider for Teradata will throw an exception when an application fails to initialize the required properties described above. For example the TdCommandBuilder.GetInsertCommand method will throw an "InvalidOperationException" when a) the TdCommandBuilder.DataAdapter property is not set or b) SelectCommand (DataAdatper.SelectCommand) of the associated TdDataAdapter is not set or c) Connection property (DataAdatper.SelectCommand.Connection) of the associated SelectCommand is not set.
The TdCommandBuilder registers with the TdDataAdapter events. At runtime, during Batch Update, the TdDataAdapter will fire the events and the TdCommandBuilder object will have an opportunity to generate Insert, Update or Delete commands based on the TdDataAdapter.SelectCommand properties and the DataRow.
Now to specify an explicit Transaction we simply add a fourth rule. An application must:
- Set the TdCommand.Transaction property of the associated TdCommand object (TdDataAdatper.SelectCommand).
The example below illustrates how to setup the TdDataAdapter.SelectCommand property and use and explicit transaction:
private static void CreateCustomerAdapterTxWithCommandBuilder(Boolean rollback) { using (TdConnection cn = new TdConnection("Data Source=TDPROD;User Id=X;Password=Y;")) { cn.Open(); TdDataAdapter adapter = new TdDataAdapter(); adapter.SelectCommand = cn.CreateCommand(); adapter.SelectCommand.CommandText = "SELECT CustomerId, CompanyName from Customers"; // Setup the DataTable schema. DataTable table = new DataTable(); adapter.FillSchema(table, SchemaType.Source); // Create the transaction and associate with the DataAdapter. adapter.SelectCommand.Transaction = cn.BeginTransaction(); // Associate CommandBuilder with the DataAdapter. TdCommandBuilder cmdBuilder = new TdCommandBuilder(adapter); // insert new rows into the DataTable. DataRow row = table.NewRow(); row["CustomerId"] = 1; row["CompanyName"] = "Teradata Corp"; table.Rows.Add(row); row = table.NewRow(); row["CustomerId"] = 2; row["CompanyName"] = "XYZ Corp"; table.Rows.Add(row); Int32 numberRows = adapter.Update(table); if (rollback) { adapter.SelectCommand.Transaction.Rollback(); } else { adapter.SelectCommand.Transaction.Commit(); } } } static void Main(string[] args) { try { using (TdConnection cn = new TdConnection("Data Source=TDPROD;UserId=X;Password=Y;")) { cn.Open(); TdCommand cmd = cn.CreateCommand(); cmd.CommandText = "SELECT CAST(COUNT(*) as Decimal(38,0)) from Customers"; Console.WriteLine("Number of Rows = {0}", cmd.ExecuteScalar()); CreateCustomerAdapterTxWithCommandBuilder(true); Console.WriteLine("Number of Rows after DataAdapter Update followed by Rollback = {0}", cmd.ExecuteScalar()); CreateCustomerAdapterTxWithCommandBuilder(false); Console.WriteLine("Number of Rows after DataAdapter Update followed by Commit = {0}", cmd.ExecuteScalar()); } } catch (Exception e) { e.ToString(); } }