【问题标题】:Getting concurrency error on updating record with data adapter使用数据适配器更新记录时出现并发错误
【发布时间】:2016-10-21 20:07:04
【问题描述】:

这是我的桌子:

学生StudentId int PK autoincrement,Name varchar(20)

当我尝试更新最后添加的记录时,出现错误:

错误Concurrency violation: the UpdateCommand affected 0 of the expected 1 records.

这是我的代码:

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();
            }  

谁能告诉我为什么会这样?有什么办法可以解决这个问题?

【问题讨论】:

  • 您修改了同一行两次。那是故意的吗?还是打算修改最后一行?
  • 我想,如果要修改lastrow,int lastrow = dt.Rows.Count - 1; row[lastrow]["Name"] = "pqr";
  • @PanagiotisKanavos:首先我想添加记录,然后我想更新最近添加的记录,实际上我试图了解 rowstate 的过程,这就是我这样做的原因
  • @sowjanyaattaluri :我想修改数据表和数据库表中的最后一行
  • 需要在数据库表中编写查询修改。

标签: c# .net datatable ado.net dataadapter


【解决方案1】:

Generating Commands with CommandBuilders MSDN 主题中所述,命令生成器生成的自动命令不会检索插入记录的标识字段:

您可能希望将输出参数映射回数据集的更新行。一项常见任务是从数据源中检索自动生成的身份字段或时间戳的值。默认情况下,DbCommandBuilder 不会将输出参数映射到更新行中的列。在这种情况下,您必须明确指定您的命令。

Retrieving Identity or Autonumber Values话题,原来基本上需要手动生成插入命令。

您可以通过以下方式为您的表执行此操作(请参阅代码中的 cmets):

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();
}  

【讨论】:

  • 这就是在更新我的最后一条记录期间 ado.net 无法找到最后一条记录的 Id 的原因,因为该记录已在数据库中提交,但正如 @Amy 所指出的那样在数据表中仍然不存在??
  • 是的。这是数据库自动生成的 ID 列的特定问题。只有在插入(添加新)并且必须以某种方式读回生成的值时才会发生这种情况(因为更新、删除等确实需要该 id 才能将表记录与相应的数据库记录匹配)。
  • 我很可能可以,但请填写另一个问题,以便我(或其他人)可以回答。
  • 好吧,你必须绑定命令中使用的所有参数,例如updateCommand.Parameters.Add("@Id", SqlDbType.Int, 0, "StudentId");。并且不要忘记将您创建的命令分配给adapter.UpdateCommand
  • 您应该问自己您是否要手动创建 UpdateCommand。正如我在答案中提到的,只需要 InsertCommand ,其他可以由命令生成器处理。
【解决方案2】:

当您进行第一次更新时,该行将写入数据库,并根据AUTOINCREMENT 获取一个主键。但是,DataTable 内的行不反映更改后的 ID。因此,当您尝试进行第二次更新时,它找不到您要更新的行(数据表中的 ID 与数据库中的 ID 不匹配)。因此,您会遇到并发错误。

因此,为了将 ID 放入 DataTable,您需要在进行第二次更新之前刷新 DataTable 的内容。

要刷新DataTable,请调用:

adapter.Fill()

欲了解更多信息,请阅读Merging DataSet Contents

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-20
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    相关资源
    最近更新 更多