Mentioned
In
|
 |
 |
 |
 |
Microsoft Knowledge Base Article
This article contents is Microsoft Copyrighted material.
©2005-©2007 Microsoft Corporation. All rights reserved. Terms
of Use |
Trademarks
Article ID: 915880 - Last Review: December 3, 2007 - Revision: 1.3 FIX: The DataAdapter object does not throw a DBConcurrencyException exception as expected when rows in a batch update do not updateOn This PageSYMPTOMSIn the Microsoft .NET Framework 2.0, when rows in a batch update do not update, the DataAdapter object does not throw a DBConcurrencyException exception as expected. Instead, the DataAdapter object continues to submit changes. This problem occurs when all the following conditions are true: - You use either an SqlDataAdapter object or an OracleDataAdapter object to perform the batch update.
- The batch update fails because another user modifies rows.
RESOLUTIONA supported hotfix is now available from Microsoft, but it is only intended to correct the problem that is described in this article. Only apply it to systems that are experiencing this specific problem. This hotfix may receive additional testing. Therefore, if you are not severely affected by this problem, we recommend that you wait for the next service pack that contains this hotfix. To resolve this problem immediately, contact Microsoft Product Support Services to obtain the hotfix. For a complete list of Microsoft Product Support Services telephone numbers and information about support costs, visit the following Microsoft Web site: Note In special cases, charges that are ordinarily incurred for support calls may be canceled if a Microsoft Support Professional determines that a specific update will resolve your problem. The usual support costs will apply to additional support questions and issues that do not qualify for the specific update in question. PrerequisitesYou must apply the hotfix that is described in Microsoft Knowledge Base article 916002.
For more information about how to obtain this hotfix, click the following article number to view the article in the Microsoft Knowledge Base:
916002Â
(http://kbalertz.com/Feedback.aspx?kbNumber=916002/
)
FIX: Error message when you try to use the SqlClient class in an ADO.NET 2.0-based application to connect to an instance of SQL Server 2005: "New request is not allowed to start because it should come with valid transaction descriptor"
Download informationTo resolve this problem, download the appropriate file for the operating system. For an x86-based operating system
The following file is available for download from the Microsoft Download Center: Collapse this imageExpand this image Download the NDP20-KB916002-X86.exe package now.
(http://download.microsoft.com/download/2/c/5/2c5bcdc9-3241-443a-9f96-4e0b0edfa727/ndp20-kb916002-x86.exe)
For an x64-based operating system
The following file is available for download from the Microsoft Download Center: Collapse this imageExpand this image Download the NDP20-KB916002-X64.exe package now.
(http://download.microsoft.com/download/d/3/9/d39c79df-4158-45df-9031-92c52ab0ed19/ndp20-kb916002-x64.exe)
For an IA-64-based operating system
The following file is available for download from the Microsoft Download Center: Collapse this imageExpand this image Download the NDP20-KB916002-IA64.exe package now.
(http://download.microsoft.com/download/0/0/3/00392dd3-5b4f-4d99-94e8-6827b8f8d7ef/ndp20-kb916002-ia64.exe)
For more information about how to download Microsoft support files, click the following article number to view the article in the Microsoft Knowledge Base:
119591Â
(http://kbalertz.com/Feedback.aspx?kbNumber=119591/
)
How to obtain Microsoft support files from online services
Microsoft scanned this file for viruses. Microsoft used the most current virus-detection software that was available on the date that the file was posted. The file is stored on security-enhanced servers that help prevent any unauthorized changes to the file.
STATUSMicrosoft has confirmed that this is a problem in the Microsoft products that are listed in the "Applies to" section. MORE INFORMATION
For more information, click the following article number to view the article in the Microsoft Knowledge Base:
824684Â
(http://kbalertz.com/Feedback.aspx?kbNumber=824684/
)
Description of the standard terminology that is used to describe Microsoft software updates
When an SqlDataAdapter object or an OracleDataAdapter object submits pending changes in batches, the DataAdapter assumes that the whole batch succeeded if the following conditions are true: - The database reports no errors.
- The database reports that the query affected one or more rows.
If you use concurrency checks in the updating logic of your DataAdapter object, an attempt to submit a change to a row that has been modified by another user does not fail. Instead, the attempt to submit a change to a row that has been modified by another user causes the database to report that the query affected no rows. Whenever the database reports an error or the database reports that the query affected no rows, the DataAdapter Class should throw a DBConcurrencyException exception. The DBConcurrencyException exception alerts the user that the update attempt failed. Before you apply this hotfix, you experience the following behavior: - Both the SqlDataAdapter class and the OracleDataAdapter class recognize update failures that result from a failed concurrency check when each update attempt contains only one row.
Notes- When the UpdateBatchSize property on the DataAdapter object is set to 1, each update attempt contains only one row.
- By default, the value of the UpdateBatchSize property on the DataAdapter object is set to 1.
- Both the SqlDataAdapter class and the OracleDataAdapter class do not recognize partial batch failures that are caused by a failed concurrency check. A partial batch failure occurs when at least one update attempt in the batch succeeds and at least one update attempt in the batch fails.
- Because the SqlDataAdapter class and the OracleDataAdapter class do not recognize partial batch failures, the DataAdapter object assumes that the whole batch succeeded. The DataAdapter object marks all rows for the batch as successfully updated, and the DataAdapter does not throw a DBConcurrencyException exception.
After you apply this hotfix, you experience the following behavior: - Both the SqlDataAdapter class and the OracleDataAdapter class recognize partial batch failures that are caused by a failed concurrency check.
- Both the SqlDataAdapter class and the OracleDataAdapter class throw a DBConcurrencyException exception to notify you that the update attempt failed.
Notes- This hotfix does not affect .NET data providers other than the SqlDataAdapter class and the OracleDataAdapter class. If you experience the problem that is mentioned in the "Symptoms" section when you use a .NET data provider that is not part of the .NET Framework, contact the manufacturer of the .NET data provider.
- The hotfix that is described in this article adds a protected method to the DbDataAdapter class. The protected method is named the GetBatchedRecordsAffected method. The DbDataAdapter class calls the GetBatchedRecordsAffected method to determine the success or the failure of individual entries in the batch. All .NET data providers that support update batching through the infrastructure of the DbDataAdapter class should override the GetBatchedRecordsAffected method. The GetBatchedRecordsAffected method should return True if the update attempt is handled by the database. The RecordsAffected parameter should be set to the number of rows that are affected by the update attempt. To set the Error property, use one of the following values:
- If the database reports error information for the update attempt, set the Error property to an appropriate exception.
- If the database does not report error information for the update attempt, set the Error property to one of the following values.
Collapse this tableExpand this table | Language | Value |
|---|
| C# | null | | Microsoft Visual Basic | Nothing |
REFERENCESSteps to reproduce the problemModify the connection strings on the attached file to connect to your SQL Server database or to your Oracle database. Then, run the following code.
C# codeprivate static void SQLBU412467() {
DataTable table = new DataTable("table");
table.Columns.Add("c1", typeof(int));
int affected;
ConnectionStringSettings config = ConfigurationManager.ConnectionStrings["trusted_2005"];
using(SqlConnection connection = new SqlConnection(config.ConnectionString)) {
connection.Open();
affected = (new SqlCommand("create table #table1 (c1 int)", connection)).ExecuteNonQuery();
Console.WriteLine(affected);
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.UpdateBatchSize = 5;
adapter.InsertCommand = new SqlCommand("insert into #table1 (c1) values(@p1); update #table1 set c1=@p1+1 where c1=@p1; update #table1 set c1=@p1 where c1=@p1+1; ", connection);
adapter.InsertCommand.Parameters.Add("@p1", SqlDbType.Int, 4, "c1").SourceVersion = DataRowVersion.Current;
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;
adapter.UpdateCommand = new SqlCommand("update #table1 set c1=@p1 where c1=@p2; update #table1 set c1=666 where c1=999", connection);
adapter.UpdateCommand.Parameters.Add("@p1", SqlDbType.Int, 4, "c1").SourceVersion = DataRowVersion.Current;
adapter.UpdateCommand.Parameters.Add("@p2", SqlDbType.Int, 4, "c1").SourceVersion = DataRowVersion.Original;
cmd.UpdatedRowSource = UpdateRowSource.None;
adapter.RowUpdating += delegate(object sender, SqlRowUpdatingEventArgs args) {
Console.WriteLine("StatementType={0} CommandText='{1}' @p1={2}", args.StatementType, args.Command.CommandText, args.Row[0]);
};
adapter.RowUpdated += delegate(object sender, SqlRowUpdatedEventArgs args) {
Console.WriteLine("StatementType={0} RecordsAffected={1}", args.StatementType, args.RecordsAffected);
DataRow[] rows = new DataRow[args.RowCount];
args.CopyToRows(rows);
foreach(DataRow row in rows) {
switch(row.RowState) {
case DataRowState.Added:
Console.WriteLine("\t@p1={0}", row[0, DataRowVersion.Current]);
break;
case DataRowState.Modified:
Console.WriteLine("\t@p1={0} @p2={1}", row[0, DataRowVersion.Current], row[0, DataRowVersion.Original]);
break;
case DataRowState.Deleted:
Console.WriteLine("\t@p1={0}", row[0, DataRowVersion.Original]);
break;
}
}
};
for(int i = 1; i <= 10; ++i) {
table.Rows.Add(new object[] { i });
}
affected = adapter.Update(table);
Console.WriteLine(affected);
for(int i = 0; i < 10; ++i) {
table.Rows[i][0] = 10 - i;
}
// Uncomment this code to cause failure.
//table.Rows[0].AcceptChanges();
//table.Rows[0][0] = 50;
try {
affected = adapter.Update(table);
Console.WriteLine(affected);
}
catch(Exception e) {
Console.WriteLine(e);
foreach(DataRow row in table.Rows) {
Console.WriteLine(row.RowError);
}
}
}
}Visual Basic codePrivate Sub Repro()
Dim table As DataTable = New DataTable("repro")
table.Columns.Add("c1", Type.GetType("System.Int32"))
Dim affected As Integer
Dim connectionstring As String = Configurationmanager.connectionstrings("trusted_2005").ConnectionString
Dim connection As SqlConnection = New SqlConnection(connectionstring)
connection.Open()
'Create a temporary table to reproduce the problem.
Dim cmd As SqlCommand = New SqlCommand("create table #repro (c1 int)", connection)
cmd.ExecuteNonQuery()
Dim adapter As SqlDataAdapter = New SqlDataAdapter()
adapter.UpdateBatchSize = 5
'Add logic to submit new and modified rows.
cmd = New SqlCommand("insert into #repro (c1) values(@p1);", connection)
cmd.Parameters.Add("@p1", SqlDbType.Int, 4, "c1").SourceVersion = DataRowVersion.Current
cmd.UpdatedRowSource = UpdateRowSource.None
adapter.InsertCommand = cmd
cmd = New SqlCommand("update #repro set c1=@p1 where c1=@p2;", connection)
cmd.Parameters.Add("@p1", SqlDbType.Int, 4, "c1").SourceVersion = DataRowVersion.Current
cmd.Parameters.Add("@p2", SqlDbType.Int, 4, "c1").SourceVersion = DataRowVersion.Original
cmd.UpdatedRowSource = UpdateRowSource.None
adapter.UpdateCommand = cmd
'Add 10 DataRow objects to the DataTable.
Dim i As Integer
For i = 1 To 10
table.Rows.Add(i)
Next
'Submit the 10 new rows to the database.
affected = adapter.Update(table)
Console.WriteLine("Call to DataAdapter.Update affected {0} row or rows", affected)
'Modify all rows.
i = 0
For i = 0 To 10 - 1
table.Rows(i)(0) = 10 - i
Next
'Uncomment the following lines of code to cause update failure.
'This code will cause the DataRow object to be out of sync with the corresponding
'row in the database table. The query to update the row will not modify any rows.
'This behavior should cause a DBConcurrencyException exception when you call the DataAdapter.Update method.
'table.Rows(2).AcceptChanges()
'table.Rows(2)(0) = 50
Try
'Submit the pending changes.
affected = adapter.Update(table)
Console.WriteLine("Call to DataAdapter.Update affected {0} row or rows", affected)
catch e as Exception
Console.WriteLine(e)
Dim row As DataRow
For Each row In table.Rows
Console.WriteLine(row.RowError)
Next
End Try
End Sub
APPLIES TO- Microsoft .NET Framework 2.0
| kbtshoot kbbug kbfix kbqfe kbpubtypekc kbhotfixserver KB915880 |
Community Feedback System
Very often, it takes hours to solve a problem. Very often, you've looked high
and low, and have tried a lot of solutions. When you finally found it, chances
are, it was because someone else helped you. Here's your chance to give back.
Use our community feedback tool to let others know what worked for you and what
didn't.
Please also understand that the community feedback system is not warranted to be
correct, it's simply a system that we've built to let people try and help each
other. If something in a feedback response doesn't make sense to you, or you're
not comfortable making changes that the feedback talks about (like registry
edits), please consult a professional.
Thank you for using kbAlertz.com Feedback System.
-- Scott Cate
Be the first to leave feedback, to help others about this knowledge base
article.
(Optional) Name
(Optional)
Public URL Or Email
Comments
No
HTML -- Text Only Please
|
 |
 |
 |
 |
 |
 |
 |
| |