Using StoredProceduresto Add, Modify, and RemoveRowsfromtheDatabase You can get a DataAdapter object to call stored proceduresto add, modify, and removerowsfromthe database. These procedures are called instead of the INSERT, UPDATE, and DELETE statements you've seen how to set in a DataAdapter object's InsertCommand, UpdateCommand, and DeleteCommand properties. The ability to call storedproceduresusing a DataAdapter is a very powerful addition to ADO.NET. For example, you can use a stored procedure to add a row to a table containing an identity column, and then retrieve the new value for that column generated by the database. You can also do additional work in a stored procedure such as inserting a row into an audit table when a row is modified. You'll see examples of both these scenarios in this section. Tip Usingstoredprocedures instead of INSERT, UPDATE, and DELETE statements can also improve performance. You should use storedprocedures if your database supports them. SQL Server and Oracle support stored procedures. Oracle stored- procedures are written in PL/SQL. The ProductID column of the Products table is an identity column, and you saw a number of storedprocedures in Chapter 4 , "Introduction to Transact-SQL Programming," that added a row tothe Products table and returned the ProductID. In this section, you'll see how to • Create the required storedprocedures in the Northwind database. • Set up a DataAdapter to call thestored procedures. • Add,modify,andremove a DataRow tofrom a DataTable. The C# methods shown in this section follow the same steps as shown in the earlier section, "Modifying Rows in a DataTable ." Note You'll find a complete program named PushChangesUsingProcedures.cs in the ch11 directory that illustrates the use of the methods shown in this section. The listing for this program is omitted from this book for brevity. Creating theStoredProcedures in theDatabase You'll create the following three storedprocedures in the Northwind database: • AddProduct4(), which adds a row tothe Products table. • UpdateProduct(), which updates a row in the Products table. • DeleteProduct(), which deletes a row fromthe Products table. Let's take a look at these procedures. The AddProduct4() Procedure AddProduct4() adds a row tothe Products table. It uses the number 4 because previous chapters featured procedures named AddProduct(), AddProduct2(), and AddProduct3(). Listing 11.4 shows the AddProduct4.sql file that you use to create the AddProduct4() procedure. Refer to Chapter 4 if you need a refresher on the Transact-SQL language or if you need to find out how to run this script to create the procedure in the database. Listing 11.4: ADDPRODUCT4.SQL /* AddProduct4.sql creates a procedure that adds a row tothe Products table using values passed as parameters tothe procedure. The procedure returns the ProductID of the new row using a RETURN statement */ CREATE PROCEDURE AddProduct4 @MyProductName nvarchar(40), @MyUnitPrice money AS -- declare the @MyProductID variable DECLARE @MyProductID int -- insert a row into the Products table INSERT INTO Products ( ProductName, UnitPrice ) VALUES ( @MyProductName, @MyUnitPrice ) -- use the SCOPE_IDENTITY() function to get the last -- identity value inserted into a table performed within -- the current database session andstored procedure, -- so SCOPE_IDENTITY returns the ProductID for the new row -- in the Products table in this case SET @MyProductID = SCOPE_IDENTITY() RETURN @MyProductID Note You'll find the AddProduct4.sql file in the ch11 directory. The UpdateProduct() Procedure UpdateProduct() updates a row in the Products table. Listing 11.5 shows the UpdateProduct.sql file that you use to create the UpdateProduct() procedure. Listing 11.5: UPDATEPRODUCT.SQL /* UpdateProduct.sql creates a procedure that modifies a row in the Products table using values passed as parameters tothe procedure */ CREATE PROCEDURE UpdateProduct @OldProductID int, @NewProductName nvarchar(40), @NewUnitPrice money, @OldProductName nvarchar(40), @OldUnitPrice money AS -- update the row in the Products table UPDATE Products SET ProductName = @NewProductName, UnitPrice = @NewUnitPrice WHERE ProductID = @OldProductID AND ProductName = @OldProductName AND UnitPrice = @OldUnitPrice Because the WHERE clause contains the old column values in the UPDATE statement of this procedure, the UPDATE uses optimistic concurrency described earlier. This means that one user doesn't overwrite another user's changes. The DeleteProduct() Procedure DeleteProduct() deletes a row fromthe Products table. Listing 11.6 shows the DeleteProduct.sql file that you use to create the DeleteProduct() procedure. Listing 11.6: DELETEPRODUCT.SQL /* DeleteProduct.sql creates a procedure that removes a row fromthe Products table */ CREATE PROCEDURE DeleteProduct @OldProductID int, @OldProductName nvarchar(40), @OldUnitPrice money AS -- delete the row fromthe Products table DELETE FROM Products WHERE ProductID = @OldProductID AND ProductName = @OldProductName AND UnitPrice = @OldUnitPrice Using SET NOCOUNT ON in StoredProcedures In Chapter 4 , "Introduction to Transact-SQL Programming," you saw that you use the SET NOCOUNT ON command to prevent Transact-SQL from returning the number of rows affected. Typically, you must avoid using this command in your storedprocedures because the DataAdapter uses the returned number of rows affected to know whether the update succeeded. There is one situation when you must use SET NOCOUNT ON: when your stored procedure performs an INSERT, UPDATE, or DELETE statement that affects another table besides the main one you are pushing a change to. For example, say the DeleteProduct() procedure also performed an INSERT statement to add a row tothe ProductAudit table (described in Chapter 4 ) to record the attempt to delete the row fromthe Products table. In this example, you must use SET NOCOUNT ON before performing the INSERT into the ProductAudit table, as shown in Listing 11.7 . Listing 11.7: DELETEPRODUCT2.SQL /* DeleteProduct2.sql creates a procedure that removes a row fromthe Products table */ CREATE PROCEDURE DeleteProduct2 @OldProductID int, @OldProductName nvarchar(40), @OldUnitPrice money AS -- delete the row fromthe Products table DELETE FROM Products WHERE ProductID = @OldProductID AND ProductName = @OldProductName AND UnitPrice = @OldUnitPrice -- use SET NOCOUNT ON to suppress the return of the -- number of rows affected by the INSERT statement SET NOCOUNT ON -- add a row tothe Audit table IF @@ROWCOUNT = 1 INSERT INTO ProductAudit ( Action ) VALUES ( 'Product deleted with ProductID of ' + CONVERT(nvarchar, @OldProductID) ) ELSE INSERT INTO ProductAudit ( Action ) VALUES ( 'Product with ProductID of ' + CONVERT(nvarchar, @OldProductID) + ' was not deleted' ) By using SET NOCOUNT ON before the INSERT, only the number of rows affected by the DELETE statement is returned, andthe DataAdapter therefore gets the correct value. Transact-SQL also has a SET NOCOUNT ON command to turn on the number of rows affected. You can use a combination of SET NOCOUNT OFF and SET NOCOUNT ON if you need to perform an INSERT, UPDATE, or DELETE statement before the main SQL statement in your stored procedure. Setting Up a DataAdapter to Call StoredProcedures As mentioned in the earlier section "Modifying Rows in a DataTable ," you need to create a DataAdapter object and set its SelectCommand, InsertCommand, UpdateCommand, and DeleteCommand properties with appropriate Command objects. This time, however, the InsertCommand, UpdateCommand, and DeleteCommand properties will contain Command objects that call thestoredprocedures shown earlier. First, the following example creates a SqlCommand object containing a SELECT statement and sets the SelectCommand property of a SqlDataAdapter to that SqlCommand: SqlCommand mySelectCommand = mySqlConnection.CreateCommand(); mySelectCommand.CommandText = "SELECT " + " ProductID, ProductName, UnitPrice " + "FROM Products " + "ORDER BY ProductID"; SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(); mySqlDataAdapter.SelectCommand = mySelectCommand; The SELECT statement is then run when you call the mySqlDataAdapter object's Fill() method to retrieve rowsfromthe Products table into a DataSet. Before you can push changes tothe database, you must set the InsertCommand, UpdateCommand, and DeleteCommand properties of your DataAdapter with Command objects. These Command objects will contain calls tothe AddProduct4(), UpdateProduct(), and DeleteProduct() storedprocedures that you created earlier. When you then add,modify, or remove DataRow objects from your DataSet, and then call the Update() method of your DataAdapter, the appropriate stored procedure is run to push your changes tothe database. Let's take a look at how to set the InsertCommand, UpdateCommand, and DeleteCommand properties of your DataAdapter. . Using Stored Procedures to Add, Modify, and Remove Rows from the Database You can get a DataAdapter object to call stored procedures to add, modify, and. DataAdapter to call the stored procedures. • Add, modify, and remove a DataRow to from a DataTable. The C# methods shown in this section follow the same steps