问题
This is my table:
Student:StudentId int PK autoincrement,Name varchar(20)
When i am trying to update last added records then i am geting error:
Error: Concurrency violation: the UpdateCommand affected 0 of the expected 1 records.
This is my code:
using (var connection = new SqlConnection("MyConnectionstring"))
{
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter();
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
adapter.SelectCommand = new SqlCommand("select * from Student", connection);
DataTable dt = new DataTable();
adapter.Fill(dt);
DataRow row = dt.NewRow();
row["Name"] = "Abc";
dt.Rows.Add(row);
var addedRecords = dt.GetChanges(DataRowState.Added);
adapter.Update(dt);
dt.AcceptChanges();
DataRow lastRow = dt.Rows[dt.Rows.Count - 1];
row["Name"] = "Pqr";
adapter.Update(dt); //Error Here
connection.Close();
}
Can anybody please tell me why this is happening and what can be the workaround for this problem??
回答1:
As described in the Generating Commands with CommandBuilders MSDN topic, the automatic commands generated by the command builders do not retrieve the identity fields for the inserted records:
You might want to map output parameters back to the updated row of a DataSet. One common task would be retrieving the value of an automatically generated identity field or time stamp from the data source. The DbCommandBuilder will not map output parameters to columns in an updated row by default. In this instance you must specify your command explicitly.
Looking at Retrieving Identity or Autonumber Values topic, it turns out that basically you need to generate the insert command manually.
Here is how you can do that for your table (see the comments inside the code):
using (var connection = new SqlConnection("MyConnectionstring"))
{
connection.Open();
// Create data adapter with the specified SelectCommand
var adapter = new SqlDataAdapter("select * from Student", connection);
// Build InsertCommand
var insertCommand = new SqlCommand(
"insert into Student (Name) values (@Name) SET @Id = SCOPE_IDENTITY()",
connection);
insertCommand.Parameters.Add("@Name", SqlDbType.VarChar, 20, "Name");
var parameter = insertCommand.Parameters.Add("@Id", SqlDbType.Int, 0, "StudentId");
parameter.Direction = ParameterDirection.Output;
insertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
adapter.InsertCommand = insertCommand;
// Auto build outher commands
var builder = new SqlCommandBuilder(adapter);
// Read the data
var dt = new DataTable();
adapter.Fill(dt);
// Insert a new record
var row = dt.NewRow();
row["Name"] = "Abc";
dt.Rows.Add(row);
adapter.Update(dt);
// Update the just inserted record
row["Name"] = "Pqr";
adapter.Update(dt);
connection.Close();
}
回答2:
When you do the first update, the row is written to the database and it gets a primary key per the AUTOINCREMENT
. However, the row inside the DataTable
does not reflect the changed ID. Therefore, when you try to do the second update, it can't find the row you're intending to update (the ID in the data table doesn't match the ID in the database). Consequently, you get a concurrency error.
That's why, in order to get the ID into the DataTable
, you will need to refresh the contents of the DataTable
before doing the second update.
To refresh the DataTable
, call:
adapter.Fill()
For more information, read Merging DataSet Contents.
来源:https://stackoverflow.com/questions/37924709/getting-concurrency-error-on-updating-record-with-data-adapter