Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
(ebook) Visual Studio .NET Mastering Visual Basic.pdf
Скачиваний:
136
Добавлен:
17.08.2013
Размер:
15.38 Mб
Скачать

988 Chapter 22 PROGRAMMING THE ADO.NET OBJECTS

excessively large, you can reload it from the database and save yourself some serious debugging. You can also copy the code generated by the wizard and reuse it.

Updating Tables Manually

To update the underlying table(s) from within a DataSet, you must call the DataAdapter object’s Update method. The Update method sends all the changes to the database, and through the appropriate stored procedures, the tables are updated. The DataAdapter object exposes the ContinueUpdateOnError property, which determines how the DataAdapter will react when an error is encountered. If the ContinueUpdateOnError property is False, which (mysteriously) is the default value, the DataAdapter will terminate the update process. If the first 10 edited rows in the DataSet contain no errors, they will be committed to the database. If the 11th row contains an error, the DataAdapter won’t even attempt to update the remaining rows.

The ContinueUpdateOnError property should be set to True, so that the DataAdapter will update all the rows that don’t contain errors. My guess is that all developers will make a habit of setting this property to True. After calling the Update method with the ContinueUpdateOnError property set to True, you must examine each row’s Errors property to find out whether there was an error. Even better, you can insert the appropriate code in the DataAdapter object’s RowUpdated event, which is fired after each attempt to update a row.

In this event, you can examine the e.Status property for the current operation. If the operation failed, then the e.Status property will be UpdateStatus.ErrorsOccurred. You can then retrieve the error message describing why the operation failed and present it along with the row (or the row’s key field) to the user, or take some other course of action. There are no simple rules as to how to handle update errors; it all depends on the application. A safe approach is to accept the changes in rows of the DataSet that successfully updated the underlying table(s) and reset the ones that failed to update the database to their original values. At the same time, you can create a list of all the rows that failed to update along with the corresponding error message. Let’s see how to use this technique to build a robust data-entry screen.

The DataEntry project’s main form, shown in Figure 22.4, displays the names of all products in a ListBox control. To view the fields of a product on the form, click the product’s name in the list. The technique of using a ListBox control as a navigational tool has been described earlier in this chapter. There are actually better techniques for selecting a specific row, but the focus in this example is how to edit a table of the DataSet and commit the changes to the database.

Figure 22.4

The DataEntry project demonstrates robust table-editing techniques.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

PERFORMING UPDATE OPERATIONS 989

To edit a row, you just change the values of its fields on the form. To change the product’s category and supplier, simply select another item in the corresponding ComboBox control. To delete the selected product, click the Delete Product button. To add a new product to the table, click the New Product button. When this button is clicked, the add and delete buttons are replaced by OK and Cancel buttons. Moreover, the ListBox control is disabled, so that you can’t select another row until you finish editing the new row. To commit the new row to the DataSet, click OK. This action hides the OK and Cancel buttons and restored the other two buttons on the form.

Notice that the new row is added to the DataSet, which resides on the client—no information is sent to the database server. After editing, deleting, and adding rows to the DataSet, you can commit the changes to the Products table of the DataSet by clicking the Update Table button. In this button’s event handler, the Update method of the matching DataAdapter object is called. Instead of passing the entire DataSet to the server, we call the Update method three times, passing one set of rows at a time: the edited, the added, and finally the deleted rows. Listing 22.12 is the handler of the Click event of the Update Table button.

Listing 22.12: Calling the DataAdapter’s Update Method

Private Sub UpdateTable(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnUpdate.Click

If Not DsProducts1.GetChanges(DataRowState.Modified) Is Nothing Then _ SqlDataAdapter1.Update(DsProducts1.GetChanges(DataRowState.Modified))

If Not DsProducts1.GetChanges(DataRowState.Added) Is Nothing Then _ SqlDataAdapter1.Update(DsProducts1.GetChanges(DataRowState.Added))

If Not DsProducts1.GetChanges(DataRowState.Deleted) Is Nothing Then _ SqlDataAdapter1.Update(DsProducts1.GetChanges(DataRowState.Deleted))

End Sub

The code calls the Update method with a different group of rows, only if the group isn’t empty. If there are rows to be updated, the Update method is called with a DataSet object that contains the edited (modified) rows. The GetChanges method retrieves the rows whose state is the same as the second argument.

The DataAdapter attempts to update one row at a time. After each attempt (whether it was successful or not), it fires the RowUpdated event, which is handled by the code shown in Listing 22.13.

Listing 22.13: Handling the DataAdapter’s RowUpdated Event

Private Sub SqlDataAdapter1_RowUpdated(ByVal sender As Object, _

ByVal e As System.Data.SqlClient.SqlRowUpdatedEventArgs) _ Handles SqlDataAdapter1.RowUpdated

Select Case e.Row.RowState Case DataRowState.Added

Console.Write(“Adding Product ID = “ & _ e.Row.Item(“ProductID”, ataRowVersion.Proposed))

Case DataRowState.Deleted

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

990 Chapter 22 PROGRAMMING THE ADO.NET OBJECTS

Console.Write(“Deleting Product ID = “ & _ e.Row.Item(“ProductID”, DataRowVersion.Original))

Case DataRowState.Modified Console.Write(“Updating Product ID = “ & _

e.Row.Item(“ProductID”, DataRowVersion.Original))

End Select

If e.Status = UpdateStatus.ErrorsOccurred Then Console.WriteLine(“Failed to update row “) Console.WriteLine(e.Errors.Message) e.Row.RejectChanges()

Else

Console.WriteLine(“ Updated successfully”) e.Row.AcceptChanges()

End If End Sub

The event reports the row being updated through the Row property of its e argument. The expression e.Row.RowState returns the state of the row. We use the row’s state to display the type of action (add/edit/delete) that caused the current row to be updated. We also display the key field’s value of the current row, so that the user knows which rows failed to update the database. Notice that new rows don’t have an original version, so we display the proposed version.

Then the code examines the e.Status property, which will be set to UpdateStatus.ErrorsOccurred if the operation failed, and display the message returned by the database. The output produced by the RowUpdated event handler looks like this:

Updating Product ID = 1 Updated successfully

Updating Product ID = 9 Updated successfully

Updating Product ID = 44 Updated successfully

Updating Product ID = 91 Updated successfully

Deleting Product ID = 78 Updated successfully

The product with ID of 91 was added to the table, but the RowUpdated event handler reports it as being updated.

For each row that successfully updates the underlying table, we call the AcceptChanges method. This method copies the original field values into the current values. For all intents and purposes, this row looks as if it was just read from the database and hasn’t been edited yet.

This techniques works only if the SqlDataAdapter1 object’s ContinueUpdateOnError property is set to True. If not, the update process will be interrupted the first time an error occurs. The project uses three DataAdapter objects, one for each of the tables Products, Categories, and Suppliers.

Of the three DataAdapter objects, only the one that corresponds to the Products table contains statements to update the database. The other two DataAdapters contain only a SELECT statement to retrieve their data from the database.

To add a new row to the Products table, you must click the New Product button. This button’s event handler prepares the various controls to accept the field values of the new row and displays the

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

PERFORMING UPDATE OPERATIONS 991

OK and Cancel buttons. Most important, it suspends the data binding. The TextBox controls on the form may assume invalid values for a while, so we must disable the data binding while the fields of the new row are being set. Data binding will resume later, when the user clicks the OK button and the new row is actually added to the DataSet. Here’s the code behind the New Product button:

Private Sub bttnNew_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnNew.Click Me.BindingContext(DsProducts1, “Products”).SuspendBinding() ListBox1.Enabled = False

bttnOK.Visible = True bttnCancel.Visible = True bttnDelete.Visible = False bttnNew.Visible = False

End Sub

Finally, when the OK button is clicked, the new row is committed to the database with the statements of Listing 22.14.

Listing 22.14: Adding a New Row

Private Sub bttnOK_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles bttnOK.Click Dim newRow As DSProducts.ProductsRow

newRow = DsProducts1.Products.NewProductsRow newRow.Item(“ProductName”) = TextBox2.Text newRow.Item(“UnitPrice”) = TextBox3.Text

If cmbCategory.SelectedValue > 0 Then _ newRow.Item(“CategoryID”) = cmbCategory.SelectedValue

If cmbSupplier.SelectedValue > 0 Then _ newRow.Item(“SupplierID”) = cmbSupplier.SelectedValue

newRow.Item(“Discontinued”) = 0 DsProducts1.Products.AddProductsRow(newRow) Me.BindingContext(DsProducts1, “Products”).ResumeBinding() ListBox1.Enabled = True

bttnOK.Visible = False bttnCancel.Visible = False bttnDelete.Visible = True bttnNew.Visible = True

End Sub

To add a new row, we must first create a variable that represents this row. Since we’re working with a typed DataSet, we can declare a variable of the NewProductsRow type. Then we set the fields of the new row and finally add the newly created row to the table with the AddProductsRow method. After adding the row to the table, we enable the data binding again with the ResumeBinding method.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com