In the previous section, you painstakingly created the Update, Insert, and Delete commands. You first created stored procedures, and then you created Command objects for each procedure, passing in the necessary parameters. The .NET Framework does a lot of this work for you, if the update, insert, and delete commands are simple enough.
The framework provides a Command Builder (SqlCommandBuilder and OleDbCommandBuilder) to generate the necessary Delete, Update, and Insert commands without your writing stored procedures. To take advantage of these objects, the following conditions must be met:
To see how using the Command Builder classes simplifies the task when these conditions are met, you'll modify the program to build the DataSet only from the Bugs table. Your user interface will be much simpler because you'll use a simple Select statement: "Select * from Bugs".
Create a new application called UpdatingDataSetsCommandBuilder with a form like that shown in Figure 20-10. Add three buttons and a DataGrid, as described in Table 20-3.
Figure 20-10. Updating DataSets Command Builder form
Control |
ID |
Text |
---|---|---|
Button |
btnUpdateDS |
Update DataSet |
Button |
btnRefreshDS |
Refresh DataSet |
Button |
btnUpdateDB |
Update Database |
DataGrid |
dgBugs |
Example 20-17 shows the complete listing in C# and Example 20-18 shows the complete listing in VB.NET. A detailed analysis follows.
Example 20-17. Using Command Builder (C#)
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Data.SqlClient; namespace UpdatingDataSetsCommandBuilderCS { public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.DataGrid dgBugs; private System.Windows.Forms.Button btnUpdateDS; private System.Windows.Forms.Button btnRefreshDS; private System.Windows.Forms.Button btnUpdateDB; private DataSet bugDS; private System.ComponentModel.Container components = null; public Form1( ) { InitializeComponent( ); RefreshDataSet( ); } protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose( ); } } base.Dispose( disposing ); } #region Windows Form Designer generated code #endregion [STAThread] static void Main( ) { Application.Run(new Form1( )); } private DataSet CreateBugDataSet( ) { // connection string to connect to the Bugs Database string connectionString = "server=YourServer; uid=sa; pwd=YourPW; " + "database=WindForm_Bugs"; // Create connection object, initialize with // connection string. Open it. System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(connectionString); // Create a SqlCommand object and assign the connection System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand( ); command.Connection=connection; command.CommandText="select * from bugs"; command.CommandType=CommandType.Text; // create a data adapter and assign the command object // and add the table mapping for bugs SqlDataAdapter dataAdapter = new SqlDataAdapter( ); dataAdapter.SelectCommand=command; dataAdapter.TableMappings.Add("Table","BugInfo"); // Create the data set and use the data adapter to fill it DataSet dataSet = new DataSet( ); dataAdapter.Fill(dataSet); return dataSet; } // close CreateBugDataSet // Update the dataset with bogus data private void btnUpdateDS_Click(object sender, System.EventArgs e) { DataTable bugTable = bugDS.Tables["BugInfo"]; bugTable.Rows[0]["Description"] = "This is a test"; DataRow newRow = bugTable.NewRow( ); newRow["Description"] = "New bug test"; newRow["Product"] = 2; newRow["Version"] = "0.01"; newRow["Reporter"] = 3; bugTable.Rows.Add(newRow); } private void btnRefreshDS_Click(object sender, System.EventArgs e) { RefreshDataSet( ); } private void RefreshDataSet( ) { bugDS = CreateBugDataSet( ); dgBugs.DataSource = bugDS.Tables[0]; } private void btnUpdateDB_Click(object sender, System.EventArgs e) { string connectionString = "server=YourServer; uid=sa; pwd=YourPW; " + "database=WindForm_Bugs"; // Create connection object, initialize with // connection string. Open it. System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(connectionString); connection.Open( ); SqlDataAdapter dataAdapter = new SqlDataAdapter("select * from Bugs", connection); SqlCommandBuilder bldr = new SqlCommandBuilder(dataAdapter); dataAdapter.DeleteCommand = bldr.GetDeleteCommand( ); dataAdapter.UpdateCommand = bldr.GetUpdateCommand( ); dataAdapter.InsertCommand = bldr.GetInsertCommand( ); SqlTransaction transaction; transaction = connection.BeginTransaction( ); dataAdapter.DeleteCommand.Transaction = transaction; dataAdapter.UpdateCommand.Transaction = transaction; dataAdapter.InsertCommand.Transaction = transaction; // try to update, if all succeed commit // otherwise roll back try { int rowsUpdated = dataAdapter.Update(bugDS,"BugInfo"); transaction.Commit( ); MessageBox.Show(rowsUpdated.ToString( ) + " rows Updated."); RefreshDataSet( ); } catch (Exception ex) { MessageBox.Show("Unable to update: " + ex.Message); transaction.Rollback( ); } // rebind the grid to show the results // grid should be unchanged dgBugs.DataSource = bugDS.Tables["BugInfo"]; } // close btnUpdateDB_Click } }
Example 20-18. Using Command Builder (VB.NET)
Imports System.Data.SqlClient Public Class Form1 Inherits System.Windows.Forms.Form Dim bugDS As DataSet #Region " Windows Form Designer generated code " Public Sub New( ) MyBase.New( ) 'This call is required by the Windows Form Designer. InitializeComponent( ) RefreshDataSet( ) #End Region Private Function CreateBugDataSet( ) As DataSet ' myConnection string to connect to the Bugs Database Dim connectionString As String = _ "server=YourServer; uid=sa; pwd=YourPW; " + _ "database=WindForm_Bugs" ' Create myConnection object, initialize with ' myConnection string. Open it. Dim myConnection As New _ System.Data.SqlClient.SqlConnection(connectionString) myConnection.Open( ) ' Create a SqlCommand object and assign the myConnection Dim command As New System.Data.SqlClient.SqlCommand( ) command.Connection = myConnection command.CommandText = "Select * from Bugs" command.CommandType = CommandType.Text ' create a data adapter and assign the command object ' and add the table mapping for bugs Dim myDataAdapter As New SqlDataAdapter( ) myDataAdapter.SelectCommand = command myDataAdapter.TableMappings.Add("Table", "BugInfo") ' Create the data set and use the data adapter to fill it Dim myDataSet As New DataSet( ) myDataAdapter.Fill(myDataSet) Return myDataSet End Function ' close CreateBugDataSet Private Sub RefreshDataSet( ) bugDS = CreateBugDataSet( ) dgBugs.DataSource = bugDS.Tables(0) End Sub ' Update the dataset with bogus data Private Sub btnUpdateDS_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpdateDS.Click Dim bugTable As DataTable = bugDS.Tables("BugInfo") bugTable.Rows(0)("Description") = "This is a test" Dim newRow As DataRow = bugTable.NewRow( ) newRow("Description") = "New bug test" newRow("Product") = 2 newRow("Version") = "0.01" newRow("Reporter") = 3 bugTable.Rows.Add(newRow) End Sub Private Sub btnRefreshDS_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRefreshDS.Click RefreshDataSet( ) End Sub Private Sub btnUpdateDB_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnUpdateDB.Click Dim connectionString As String = "server=YourServer; uid=sa; " + _ "pwd=YourPW; database=WindForm_Bugs" ' Create myConnection object, initialize with ' myConnection string. Open it. Dim myConnection As New SqlConnection(connectionString) Dim transaction As SqlTransaction myConnection.Open( ) Dim myDataAdapter As New SqlDataAdapter( _ "Select * from Bugs", myConnection) Dim bldr As New SqlCommandBuilder(myDataAdapter) myDataAdapter.DeleteCommand = bldr.GetDeleteCommand( ) myDataAdapter.UpdateCommand = bldr.GetUpdateCommand( ) myDataAdapter.InsertCommand = bldr.GetInsertCommand( ) transaction = myConnection.BeginTransaction( ) myDataAdapter.DeleteCommand.Transaction = transaction myDataAdapter.UpdateCommand.Transaction = transaction myDataAdapter.InsertCommand.Transaction = transaction ' try to update, if all succeed commit ' otherwise roll back Try Dim rowsUpdated As Int16 = myDataAdapter.Update(bugDS, "BugInfo") transaction.Commit( ) MessageBox.Show(rowsUpdated.ToString( ) + " rows Updated.") RefreshDataSet( ) Catch ex As Exception MessageBox.Show("Unable to update db!" + ex.Message) transaction.Rollback( ) End Try ' rebind the grid to show the results ' grid should be unchanged dgBugs.DataSource = bugDS.Tables("BugInfo") End Sub ' close btnUpdateDB_Click End Class
Because you can use the Command Builder only with very simple queries, toss out all the code that deals with the BugHistory table. Modify btnUpdateDS_Click so that you update and add fields only in Bugs:
DataRow newRow = bugTable.NewRow( ); newRow["Product"] = 2; newRow["Version"] = "0.01"; newRow["Description"] = "New bug test"; newRow["Reporter"] = 3; bugTable.Rows.Add(newRow);
Dim newRow As DataRow = bugTable.NewRow( ) newRow("Description") = "New bug test" newRow("Product") = 2 newRow("Version") = "0.01" newRow("Reporter") = 3 bugTable.Rows.Add(newRow)
Notice that you no longer delete a row. Because of referential integrity, you cannot delete the row without deleting from BugHistory, and because the Command Builder can work only with a single table at a time, you cannot delete from BugHistory. To keep this example simple, you'll just update and insert.
The important change is in btnUpdateDB_Click, which is now far easier. Simply retrieve the DataSet and set up the Connection object, just as you did previously:
string connectionString = "server=YourServer; uid=sa; pwd=YourPassword; database=ProgASPDotNetBugs"; System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(connectionString); connection.Open( );
Dim connectionString As String = "server=YourServer; uid=sa; " + _ "pwd=YourPW; database=WindForm_Bugs" Dim myConnection As New SqlConnection(connectionString) myConnection.Open( )
Then create a DataAdapter and a SqlCommandBuilder:
SqlDataAdapter dataAdapter = new SqlDataAdapter("select * from Bugs", connection); SqlCommandBuilder bldr = new SqlCommandBuilder(dataAdapter);
Dim myDataAdapter As New SqlDataAdapter( _ "Select * from Bugs", myConnection) Dim bldr As New SqlCommandBuilder(myDataAdapter)
Use the Command Builder to build the DeleteCommand, UpdateCommand, and InsertCommand objects required by the DataAdapter, which you previously built by hand:
dataAdapter.DeleteCommand = bldr.GetDeleteCommand( ); dataAdapter.UpdateCommand = bldr.GetUpdateCommand( ); dataAdapter.InsertCommand = bldr.GetInsertCommand( );
myDataAdapter.DeleteCommand = bldr.GetDeleteCommand( ) myDataAdapter.UpdateCommand = bldr.GetUpdateCommand( ) myDataAdapter.InsertCommand = bldr.GetInsertCommand( )
That's it! You are ready to enlist the commands in the transaction:
SqlTransaction transaction; transaction = connection.BeginTransaction( ); dataAdapter.DeleteCommand.Transaction = transaction; dataAdapter.UpdateCommand.Transaction = transaction; dataAdapter.InsertCommand.Transaction = transaction;
Dim transaction As SqlTransaction transaction = myConnection.BeginTransaction( ) myDataAdapter.DeleteCommand.Transaction = transaction myDataAdapter.UpdateCommand.Transaction = transaction myDataAdapter.InsertCommand.Transaction = transaction
With that done, you are ready to call Update on the DataAdapter, as you did previously:
int rowsUpdated = dataAdapter.Update(ds,"Bugs");
Dim rowsUpdated As Int16 = myDataAdapter.Update(bugDS, "BugInfo")
The CommandBuilder object has created the necessary commands on your behalf.
That's all there is to it; the Command Builder does all the work of updating the database and ensuring concurrency integrity. You can see what the Command builder has done by adding this code to the try block:
MessageBox.Show(dataAdapter.InsertCommand.CommandText); MessageBox.Show(dataAdapter.UpdateCommand.CommandText);
The result is that the Update command is displayed in a message box, as shown in Figures Figure 20-11 and Figure 20-12.
Figure 20-11. Command Builder insert command
Figure 20-12. Command Builder update command
Pay particular attention to the Update command; you can see that the Where clause is similar to the one you built earlier (this more complex command considers null fields).
Windows Forms and the .NET Framework
Getting Started
Visual Studio .NET
Events
Windows Forms
Dialog Boxes
Controls: The Base Class
Mouse Interaction
Text and Fonts
Drawing and GDI+
Labels and Buttons
Text Controls
Other Basic Controls
TreeView and ListView
List Controls
Date and Time Controls
Custom Controls
Menus and Bars
ADO.NET
Updating ADO.NET
Exceptions and Debugging
Configuration and Deployment