The example Updating Data with a Stored Procedure update-with-storedproc.aspx shows how we can use a Command object to execute a stored procedure that updates the source data.. We also
Trang 1have carried out the operation in previous versions of ADO And we can use INSERT and DELETE statements in exactly the same way as we used an UPDATE statement in this example
However, it's often preferable to use a stored procedure defined within the data store to perform data updates Stored procedures can provide a useful increase in performance, hide the structure of a database table from inquisitive users, and allow finer control over security permissions The next example demonstrates how we can use a similar technique to that above with a stored procedure instead of a SQL statement
Using a Stored Procedure with a Command Object
Using a stored procedure with a Command object is a fundamentally similar process to using a SQL statement, as we discovered in the previous chapter when we were extracting data from a data store The example Updating Data with a
Stored Procedure ( update-with-storedproc.aspx) shows how we can use a Command object to execute a stored procedure that updates the source data
The stored procedure named AddNewBook is created within the WroxBooks database by the SQL script we provide in the samples It inserts a new row into the BookList table using values provided in parameters to the stored procedure, and returns zero (0) if it succeeds in inserting the new row:
However, to make the process repeatable when you are experimenting with the samples, we've added a rather unusual twist to the procedure (one which is unlikely to be found in a real-world application) If we hadn't done this, you would only
be able to run the procedure once unless you manually deleted the row in the database, or edited the procedure to insert
a different row
What the procedure does is to first check to see if a book with the specified ISBN (the primary key of the table) already exists If it does exist, it deletes this row from the table instead - and returns minus one (-1) as the result This way, you can run the page as many times as you wish:
Trang 2The AddNewBook Stored Procedure
The stored procedure takes as input parameters the ISBN code, title, and publication date of the book to be inserted, and
it has a fourth Integer-type output parameter to hold the result This is what it looks like:
CREATE PROCEDURE AddNewBook
@ISBN varchar(12), @Title varchar(100), @Date datetime,
@Result integer output AS
SELECT ISBN FROM BookList WHERE ISBN=@ISBN
IF @@ROWCOUNT = 0
BEGIN
INSERT INTO BookList(ISBN, Title, PublicationDate)
VALUES (@ISBN, @Title, @Date)
SELECT @Result = 0
Trang 3The Code for the Stored Procedure Update Example
In this example we're executing a stored procedure, so our command text is just the name of the stored procedure - AddNewBook We start by specifying this and displaying it in the page:
'specify the stored procedure name
Dim strSQL As String = "AddNewBook"
outSQL.InnerText = strSQL 'and display it
Now we create our connection and command objects as before However, for maximum execution efficiency, we need to specify this time that the command text is the name of a stored procedure:
Dim objConnect As New OleDbConnection(strConnect)
Dim objCommand As New OleDbCommand(strSQL, objConnect)
objCommand.CommandType = CommandType.StoredProcedure
Creating the Parameters
Next we create the parameters we'll need within the Parameters collection of the Command object The first is for the
Trang 4ISBN code and is of type OleDbType.VarChar and length 12 characters We also specify that it's an input parameter, and set the value:
'create a variable to hold a Parameter object
Dim objParam As OleDbParameter
'create a new Parameter object named 'ISBN' with the correct data
'type to match a SQL database 'varchar' field of 12 characters
objParam = objCommand.Parameters.Add("ISBN", OleDbType.VarChar, 12)
'specify that it's an Input parameter and set the value
objParam.Direction = ParameterDirection.Input
objParam.Value = "1999999999"
The process is repeated for the next two input parameters, the book title and publication date Note that the publication date parameter (named Date) is of type OleDbType.DBDate, and we have to specify the value in a format that corresponds to the column in the database In the case of a SQL datetime column, the format "yyyy-mm-dd" will work:
'create a new Parameter object named 'Title' with the correct data
'type to match a SQL database 'varchar' field of 50 characters
'specify that it's an Input parameter and set the value
objParam = objCommand.Parameters.Add("Title", OleDbType.VarChar, 50)
objParam.Direction = ParameterDirection.Input
objParam.Value = "Programming in the Virtual World"
'create another input Parameter object named 'Date' with the correct
Trang 5'data type to match a SQL database 'datetime' field
'specify that it's an Input parameter and set the value
objParam = objCommand.Parameters.Add("Date", OleDbType DBDate)
objParam.Direction = ParameterDirection.Input
objParam.Value = "2001-05-01"
The final parameter is named Result, and is an output parameter that will return the result of executing the stored procedure It returns an integer value, and so we specify OleDbType.Integer in this case:
'create an output Parameter object named 'Result' with the correct
'data type to match a SQL database 'integer' field
'specify that it's an Output parameter so no value required
objParam = objCommand.Parameters.Add("Result", OleDbType.Integer)
objParam.Direction = ParameterDirection.Output
Before executing the stored procedure, we display the input parameter values in the page - within a <div> element named outInParams We can read their current values directly from the Parameters collection by specifying the name
of each one:
'display the value of the input parameters
outInParams.InnerText = "ISBN='" & objCommand.Parameters("ISBN").Value _
& "' Title='" & objCommand.Parameters("Title").Value _
& "' Date='" & objCommand.Parameters("Date").Value & "'"
Trang 6Executing the Stored Procedure
The next step is to execute the stored procedure In this case, we don't have any returned value for the number of rows affected, so we don't need to capture the result of the ExecuteNonQuery method:
Try
objConnect.Open()
objCommand.ExecuteNonQuery()
objConnect.Close()
Catch objError As Exception
outError.InnerHtml = "* Error while updating original data.<br />" _
& objError.Message & "<br />" & objError.Source
Exit Sub
End Try
Once the stored procedure has been executed, the parameter named Result will contain the result of the process We collect its value from the Parameters collection of the Command object In our example, we also display the value - plus
an accompanying explanation message - in a <div> element named outOutParams within the page:
'collect and display the value of the output parameter
Dim intResult As Integer = objCommand.Parameters("Result").Value
Dim strResult As String = "Result='" & CStr(intResult) & "'<br />"
If intResult = 0 Then
Trang 7strResult += "Successfully inserted new book details"
Else
strResult += "Failed to insert new book details and instead " _
& "deleted existing record with this ISBN"
End If
outOutParams.InnerHtml = strResult
Updating Data Sources with Transactions
One of the features of most database systems, and some other types of data store, is the ability to use transactions Simply put, a transaction is a series of events that are all completed, or of which none are completed - there is never an intermediate result where some but not all of the events within the transaction occur
The name 'transaction' comes from real-world scenarios such as purchasing an item in a store where you give the seller money in exchange for goods Unless one of you gets cheated, the transaction will either succeed with both parties happy
at the outcome (you pay your money and get your goods), or fail where neither action occurs There should never be an outcome where you pay money and don't get the goods, or where you get goods but don't pay the money
In this section, we'll look at two types of transactions:
Database transactions, where database-specific statements control the transaction, and it is carried out within the database itself Usually the stored procedure within the database contains the transaction statements
Connection-based transactions, where the statements that control the transaction, and the execution and management of that transaction, are outside the database Usually these are a feature of the Connectionobject that executes a SQL statement or stored procedure
While it is possible to write stored procedures that perform transactions across different databases on the same server, this is outside the scope of this chapter It is also possible to use the services of another application, such as Windows
2000 Component Services (or MTS in Windows NT4) to perform a distributed transaction, where a series of events spread across different databases and applications on different servers are managed as a single transaction Chapter 17 (".NET Components") briefly looks at this topic
Trang 8Database Transactions
In a database system such as SQL Server, we specify transaction operations within a stored procedure using
vendor-specific statements like BEGIN TRANSACTION to start a new transaction, COMMIT TRANSACTION to accept all the updates and permanently commit the changes to the data, and ROLLBACK TRANSACTION to cancel all the changes made within the current transaction
We've provided an example page that uses a transacted stored procedure The stored procedure, named
DoBookArchive, is created within the WroxBooks database by the SQL script we provide with the samples
The DoBookArchive Stored Procedure
The DoBookArchive stored procedure moves a row from the BookList table into another table named ArchiveBooks, within the same database If the process succeeds, the transaction is committed and the updates are permanently applied
to the database tables If there is an error when writing to the ArchiveBooks table, or when deleting the book from the BookList table, both actions are rolled back and the tables are left in exactly the same state as before - neither is affected by the procedure
However, to make it repeatable while you are experimenting with the example, the stored procedure always starts by deleting any existing book with the same ISBN (the primary key) in the ArchiveBooks table This action will also be rolled back if the complete transaction fails, so if a book has been archived (and hence deleted from the BookList table)
it will not be deleted from the ArchiveBooks table if you run the stored procedure again with the same ISBN In this case, the INSERT statement will fail because the book is not in the BookList table, and so the entire transaction is rolled back undoing the DELETE operation on the ArchiveBooks table
This is the code for the stored procedure:
CREATE PROCEDURE DoBookArchive
@ISBN varchar(12), @Result integer output AS
DECLARE @verror int
BEGIN TRANSACTION
DELETE FROM ArchiveBooks WHERE ISBN=@ISBN
INSERT INTO ArchiveBooks (ISBN, Title, PublicationDate)
Trang 9SELECT * FROM BookList WHERE ISBN LIKE @ISBN
SELECT @verror = @@ERROR, @Result = @@ROWCOUNT
IF @verror <> 0 GOTO on_error
IF @Result > 0
BEGIN
DELETE FROM BookList WHERE ISBN=@ISBN
IF @@ERROR <> 0 GOTO on_error
The Transacted Stored Procedure Example
The example page Updating Data with a Transacted Stored Procedure (transacted-storedproc.aspx) uses the stored procedure we've just described We've arranged for it to use the same ISBN code as the previous example that
Trang 10inserts and deletes a book in the BookList table, so that you can see the results of this example by running it after inserting the new book and after deleting it Providing that you have run the previous example to insert the new book row, the stored procedure in this example will succeed:
If you then run the page again, it will show that the stored procedure failed to find the book in the BookList table (because, of course, it's just been moved to the ArchiveBooks table):
The Code for the Transacted Stored Procedure Example
As in our earlier examples, we start by specifying the name of the stored procedure and displaying it in the page, and then create the Connection and Command objects we'll need to execute it We also set the CommandType of the Command
Trang 11object to indicate that we'll be executing a stored procedure:
'specify the stored procedure name
Dim strSQL As String = "DoBookArchive"
outSQL.InnerText = strSQL 'and display it
Dim objConnect As New OleDbConnection(strConnect)
Dim objCommand As New OleDbCommand(strSQL, objConnect)
objCommand.CommandType = CommandType.StoredProcedure
Now we create the parameters for the command This time there are only two - an input parameter to hold the ISBN of the book we want to archive, and an output parameter to hold the result:
Dim objParam As OleDbParameter
'create an input Parameter object named 'ISBN' with the correct data
'type to match a SQL database 'varchar' field of 12 characters
objParam = objCommand.Parameters.Add("ISBN", OleDbType.VarChar, 12)
objParam.Direction = ParameterDirection.Input
objParam.Value = "199999999"
'create an output Parameter object named 'Result' with the correct
'data type to match a SQL database 'integer' field
'specify that it's an Output parameter so no value required
Trang 12objParam = objCommand.Parameters.Add("Result", OleDbType.Integer)
objParam.Direction = ParameterDirection.Output
'display the value of the input parameter
outInParams.InnerText = "ISBN='" & objCommand.Parameters("ISBN").Value & "'"
The next step is to open our connection and execute the stored procedure:
Try
objConnect.Open()
objCommand.ExecuteNonQuery()
objConnect.Close()
Catch objError As Exception
outError.InnerHtml = "* Error while updating original data.<br />" _
& objError.Message & "<br />" & objError.Source
Exit Sub 'stop execution
End Try
Then we can collect the result from the output parameter and display it, along with some accompanying explanatory text:
'collect and display the value of the output parameter
Dim intResult As Integer = objCommand.Parameters("Result").Value
Dim strResult As String = "Result='" & CStr(intResult) & "'<br />"
Trang 13Select Case intResult
Case -1: strResult += "Error occurred while attempting archive"
Case 0: strResult += "Failed to archive book -no matching book found"
Case > 0: strResult += "Successfully archived the specified book"
End Select
outOutParams.InnerHtml = strResult
Notice that we didn't have to do anything extra to benefit from the transaction within the stored procedure - we just executed it and checked the result to see what actually happened This is not the case, however, when we use the other type of transaction, a connection-based transaction We'll see how different working with this type of transaction is next
Connection-based Transactions
The previous example demonstrates how we can use a transaction within a stored procedure (a database transaction) to ensure that a series of operations on our data either all succeed or are all rolled back A second way of using a transaction
is through the capabilities of the Connection object
Both the SqlConnection and the OleDbConnection objects can be used to perform transacted data updates While the way we actually apply a transaction is different from the stored-procedure transaction we used in the previous example, the terminology is broadly the same:
Connection.BeginTransaction Starts a new transaction on this connection and all subsequent changes to the data
become part of the transaction until it is committed or rolled back
Transaction.Commit Commits all changes made to the data within this transaction since it was started
The changes are made permanent in the target data store
Transaction.Rollback Abandons all changes made to the data within this transaction since it was started
The changes are removed from the target data store
The Transaction Object
In ADO.NET, we have two objects that implement transactions - SqlTransaction for use with Microsoft SQL Server via TDS, and OleDbTransaction for use with an OLE-DB provider (there is also an equivalent OdbcTransaction object currently under development) To start a transaction we call the BeginTransaction method of the current
Connection object This returns a Transaction object that we must then assign to any Command objects that we want
Trang 14to enroll into that transaction
To end a transaction and commit all the changes to the database, we call the Commit method of the Transaction object (note that it's not a method of the Connection object as you might at first have expected) To abandon all changes to the data, we call the Transaction object's Rollback method instead
Notice also that we have to manually enroll any Command objects into the transaction While this might seem odd, it does allow us to have multiple transactions in progress, and use whichever is appropriate for each command we carry out on the database We can also create a nested transaction (that is a transaction that executes within another transaction) by creating a new Transaction object and calling the Begin method
A Connection-based Transaction Example
To see the transaction methods in action, open the example Transactional Data Updates with a Command Object (update-with-transaction.aspx) This page creates three SQL statements that are used to update the titles of three books in the BookList table to reflect the current date and time, and then it executes these statements Afterwards, it reads the rows back from the database and displays the details to confirm that the updates were successful:
You can see in the previous screenshot that the transaction was committed, and the three rows were updated However, this is only because the page contains logic that uses the current time in seconds to decide whether to commit or roll back the transaction While not a real-world scenario, it is done so that you can see the result of rolling back a transaction as well as committing it After running the page again where the time has an even number of seconds, the transaction is rolled back and so the titles are not updated:
Trang 15The Code for the Connection-based Transaction Example
The only real differences in the way that this page works, when compared to the other examples that use SQL statements
to update the data source, is that we have to call the transaction methods at the appropriate times - effectively managing the transaction ourselves Instead of a stored procedure within the database itself deciding whether to commit or rollback the changes (usually dependent on the outcome of one of the statements in the stored procedure), we decide within our ASP code if the transaction should be committed or rolled back
As usual, we start by creating the SQL statements we'll be executing against the BookList table to update the book titles:
'specify the SQL statements to update the data
Dim strNow, strSQL1, strSQL2, strSQL3 As String
Dim datNow As DateTime = Now()
strNow = datNow.ToString("dd-M-yy \a\t hh:mm:ss")
strSQL1 = "UPDATE BookList SET Title = 'Book One Written on " _
Trang 16& strNow & "' WHERE ISBN='1861009901'"
outSQL1.InnerText = strSQL1 'and display it
strSQL2 = "UPDATE BookList SET Title = 'Book Two Written on " _
& strNow & "' WHERE ISBN='1861009902'"
outSQL2.InnerText = strSQL2 'and display it
strSQL3 = "UPDATE BookList SET Title = 'Book Three Written on " _
& strNow & "' WHERE ISBN='1861009903'"
outSQL3.InnerText = strSQL3 'and display it
Then we create our Connection and Command objects, and declare a variable to hold the number of rows affected by our updates We've set the initial value to zero here, though this is not actually required (zero is the default value), but it helps
to illustrate how the code works, and ensures that we can safely add on the result each time we execute a SQL statement:
Dim objConnect As New OleDbConnection(strConnect)
Dim objCommand As New OleDbCommand()
Dim intRowsAffected As Integer = 0
Starting a Transaction
We need a variable to hold the Transaction object that will be returned when we start a transaction, and so we declare this next:
'declare a variable to hold a Transaction object
Dim objTransaction As OleDbTransaction
Trang 17Next we open our connection, and execute the BeginTransaction method to start a new connection-based transaction
We assign the Transaction object that is returned to our objTransaction variable:
'specify the Connection object and command type for the Command
objCommand.Connection = objConnect
objCommand.CommandType = CommandType.Text
'attach the current transaction to the Command object
'must be done after setting Connection property
objCommand.Transaction = objTransaction
Notice that we can only do so after we've set the Connection property, and if we want to change the Connection
property afterwards we first have to un-enrol it by setting the Transaction property of the Command to Nothing
Then we can assign each SQL statement to the CommandText property in turn and execute it:
'specify the select statement to use for the first update
Trang 18objCommand.CommandText = strSQL1
'execute the SQL statement against the command to fill the DataReader
'keep track of number of records originally updated
Transaction object to cancel any changes that have been applied to the source data:
Catch objError As Exception
'error encountered so roll back all the updates
objTransaction.Rollback()
'display error details
outError.InnerHtml = "* Error while updating original data.<br />" _
& objError.Message & "<br />" & objError.Source
Trang 19Exit Sub ' and stop execution
End Try
After successfully executing all three statements without an error, we would normally call the Commit method of the Transaction object to permanently apply the changes to the data store However, in our example, we check the number of seconds in the current time and only call Commit if this is an odd number If it's an even number, we call the Rollback method to abandon all updates:
'all seems OK so can now commit all the updates However as an
'illustration of the technique only do so if the current time
'has an odd number of seconds If not, rollback all changes
Dim strCommit As String
Afterwards we can read the values of the rows using a DataReader object and display them in the page This is identical
to the way we did it in the first example in this chapter, so we aren't repeating the code here
Having looked briefly at how we can use transactions to ensure multiple data updates all succeed, or all fail, we'll move
on to a different topic The DataSet object we introduced in previous chapters has the facility to automatically update the source data from which it was created - or in fact any data store for which the structure and contents of the tables within
Trang 20the DataSet are of the appropriate format This is the focus of the next section
Updating Data from a DataSet Object
In previous chapters we've regularly used a DataSet object to store data extracted from a database, or to hold data that we've created dynamically using code We also looked at the ways we can edit and modify the data that the DataSetcontains In this section of the chapter, we'll look in detail at how we get those changes back into a data source such as
a relational database
ADO.NET includes the DataAdapter object, which is used to provide the connection between a data store and a disconnected DataSet We saw this object in action in Chapter 8, but only so far as collecting rows from a database and pushing them into a DataSet To understand how the update process works for a DataSet, we need to examine the DataAdapter object in more depth
Inside the DataAdapter Object
In order to understand and take advantage of many of the features of the NET disconnected data model, especially when
we come to look at concurrent data update issues later in this chapter, you must be comfortable with what's going on behind the scenes when you use the DataSet and DataAdapter objects to push changes made to the data back into a data source
The full chain of objects that are required to pull data from a data store into a DataSet, and push the changes back into the data store after updating, is shown in the next schematic You can see the four main objects that are involved in the process - the Connection, Command, DataAdapter, and DataSet: