In the previous section, you read data from the database into aDataSet, updated the data in the DataSet, and then wrote thechanges back to the database.. When one user works with a recor
Trang 1In the previous section, you read data from the database into aDataSet, updated the data in the DataSet, and then wrote thechanges back to the database In a real-world application, itwould be possible for other people to read the same data into
DataSets of their own, edit their data, and write their changes
back to the database
You can easily imagine that this possibility could possibly causetremendous data corruption Imagine, for example, that a
quality assurance person downloads the current open bugs with
an eye toward updating some of the information Meanwhile,across the office (or across town) a developer has downloadedand is reviewing a few open bugs Both of them are reading bug
Trang 2was Jesse and the Severity was High These earlier values arewritten over the values updated by QA, and the QA edits are
lost The technical term for this is bad.
To prevent this kind of problem, use any of the following
strategies:
1 Lock the records When one user works with a record, other users can read the records but cannot update them.
locked, but many surrounding records are locked as well
While record and page locking is not uncommon in some
database environments, it is generally undesirable It's possiblefor a record to be locked, and the user never to return to thedatabase to unlock it (if the user goes to lunch or her computer
Trang 3processes that keep track of how long records have been
locked, and unlock records after a time-out period
As you saw in the previous example, a single query may touchmany records in many tables If you were to lock all those
records for each user, it wouldn't take long before the entiredatabase was locked In addition, it often isn't necessary Whileeach user may look at dozens of records, each user usuallyupdates only a very few Locking is a very big, blunt weapon;what is needed is a small, delicate surgical tool
The value that is now in the DataSet because you havechanged it
The DataSet provides support for this approach even though it
is not an efficient way to manage data updates This approachinvolves creating an event handler for the RowUpdating event.The event handler examines the original value of each field andqueries the database for the value currently in the database Ifthese values are different, then someone has changed the
database since the DataSet was filled, and you can take
corrective action
You will find two significant problems with this approach First,
Trang 4This approach is very efficient In most cases, your update willsucceed, and you will not have bothered with extra reads of thedatabase If your update succeeds, there is no lag between
checking the data and the update, so there is no chance of
someone sneaking in another write Finally, if your update fails,you know why, and you can take corrective action
For this approach to work, your stored procedure for updatesmust fail if the data has changed in the database since the timeyou retrieved the DataSet Since the DataSet can tell you theoriginal values it received from the database, you can pass
those values back into the stored procedure as parameters, andthen add them to the Where clause in your update statement,
as shown in the spUpdateBugFromDataSetWithConcurrencystored procedure listed in Example 20-14
Example 20-14 Updating with concurrency
CREATE PROCEDURE spUpdateBugFromDataSetWithConcurrency
@ProductID int,
Trang 6BugHistory table
The transaction tests for errors If no rows match, there is no error and the transaction will continue You must make sure that at least one row was added to Bugs before updating the BugHistory.
You can test for how many rows were added altogether in the
RowUpdated event handler If no row was updated, you can
assume that it was because the original row was changed, andyou can take appropriate corrective action
Trang 7It is possible for the update to Bugs to work, yet the update to BugHistory fails The program will return one updated record For simplicity, this example does not handle that permutation A well- crafted update statement could catch this problem, but at the cost of making the code more difficult to understand.
The complete listing is shown in Example 20-15 for C# and
Trang 10string cmd = "Update Bugs set Product = 2 where BugID = 1";
Trang 11param.SourceVersion=DataRowVersion.Original;
param =
Trang 12updateCmd.Parameters.Add("@Description",SqlDbType.Text,8000); param.Direction = ParameterDirection.Input;
Trang 14param.SourceColumn="BugHistoryID";
param.SourceVersion=DataRowVersion.Original; // note Original
Trang 16// as a string
string s = "Attempted " +
Trang 22
param = updateCmd.Parameters.Add("@OldReporter", SqlDbType.Int) param.Direction = ParameterDirection.Input
param.SourceColumn = "StatusID"
param.SourceVersion = DataRowVersion.Original
param = updateCmd.Parameters.Add("@Severity", SqlDbType.Int) param.Direction = ParameterDirection.Input
param.SourceColumn = "SeverityID"
param.SourceVersion = DataRowVersion.Current
param = updateCmd.Parameters.Add("@OldSeverity", SqlDbType.Int) param.Direction = ParameterDirection.Input
param.SourceColumn = "SeverityID"
param.SourceVersion = DataRowVersion.Original
Trang 23
param.SourceColumn = "BugHistoryID"
param.SourceVersion = DataRowVersion.Original ' note Original
param = deleteCmd.Parameters.Add("@BugHistoryID", SqlDbType.Int) param.Direction = ParameterDirection.Input
param.SourceColumn = "BugHistoryID"
param.SourceVersion = DataRowVersion.Original ' note Original
Trang 26System.Enum.GetName(e.StatementType.GetType( ), e.StatementType) & _ " "
Trang 29MessageBox.Show(s & "Concurrency error updating BugID: " & _ e.Row("BugID", DataRowVersion.Original))
Trang 30string cmd = "Update Bugs set Product = 2 where BugID = 1";SqlCommand cmd1 = new SqlCommand(cmd,connection2);
cmd1.ExecuteNonQuery( );
Dim myConnection2 As _
New System.Data.SqlClient.SqlConnection(connectionString)myConnection2.Open( )
Trang 31field in Bugs rather than in BugHistory In previous examples, you wrote:
bugTable.Rows[0]["Response"] =
"This is a test";
In this example, you will modify it to:
bugTable.Rows[0]["ReporterID"] = "1";