WinForms Binding Source Navigator并发错误排查求助
看起来你遇到的问题虽然标注为“并发错误”,但因为你是唯一用户,本质是DataSet行状态管理和数据库行版本不一致导致的,而非真正的多用户并发。结合你的代码,我来梳理几个可能的原因和对应的解决方案:
1. 手动调用AcceptChanges()是大概率的元凶
你的updateDataItems()方法最后调用了xyzDataSet.AcceptChanges(),这会把整个DataSet里所有行的RowState强制设为Unchanged,并且将行的Original值覆盖为Current值。这会干扰TableAdapter的正常状态追踪:
- 第一次更新
CUSTOMER字段后,AcceptChanges()会把该行的Original值同步为新的CUSTOMER值,但数据库里的ACCT_NO还是旧值; - 当你修改
ACCT_NO再次调用updateDataItems()时,TableAdapter会用当前的Original值(包括新CUSTOMER、旧ACCT_NO)去匹配数据库行,但如果你的TableAdapter并发检查逻辑是“对比所有字段”,可能因为其他隐含字段(比如最后修改时间)未同步,导致匹配失败。
修复方案:移除全局的AcceptChanges()调用,因为TableAdapter的Update()方法会自动对成功更新的行调用AcceptChanges(),不需要手动全局执行:
void updateDataItems() { this.procBindingSource.EndEdit(); this.procTableAdapter.Update(xyzDataSet); // 删掉这行:xyzDataSet.AcceptChanges(); }
2. 手动BeginEdit/EndEdit与BindingSource的编辑状态冲突
你在代码中手动对DataRowView调用了BeginEdit()和EndEdit(),但BindingSource本身会自动管理行的编辑状态(当你修改DataRowView的字段时,它会自动进入编辑状态)。嵌套的手动编辑操作可能导致行状态混乱,进而引发更新时的版本不匹配。
修复方案:去掉不必要的手动编辑控制,简化业务代码:
// save current work updateDataTable(); // Open a window and get new customer info // CurrentCustomer is returned from the opened window using (SqlConnection cx = new SqlConnection(GetConnectionString())) { DataRowView dataRow = (DataRowView)procBindingSource.Current; // 直接修改字段,BindingSource会自动处理编辑状态 dataRow["CUSTOMER"] = CurrentCustomer; updateDataItems(); SqlCommand cmd = new SqlCommand(@"select acct_no from cust_processing where id = @id", cx); cmd.Parameters.AddWithValue("@id", (int)dataRow["ID"]); cx.Open(); var results = cmd.ExecuteScalar(); if (results != null) { dataRow["ACCT_NO"] = results.ToString(); updateDataItems(); } }
3. updateDataTable()的实现可能存在数据覆盖问题
你提到updateDataTable()是为了确保使用最新版本的行,如果它的逻辑是重新从数据库填充DataSet,那必须先保存当前未提交的修改,否则新填充的数据会覆盖你之前的编辑,导致后续更新时版本不匹配。
检查并修复updateDataTable():
void updateDataTable() { // 先保存未提交的修改 updateDataItems(); // 清空旧数据再填充新数据 xyzDataSet.proc.Clear(); procTableAdapter.Fill(xyzDataSet.proc); }
4. 配置TableAdapter的刷新与并发检查策略
打开你的DataSet设计器,右键点击procTableAdapter选择「配置」,进入「高级选项」:
- 勾选「刷新数据表」:确保
Update()之后,TableAdapter会自动从数据库获取最新的行数据(包括timestamp、自增字段等),同步到DataSet中; - 调整并发模式:如果你的表有timestamp字段,选择「仅比较时间戳」会比「比较所有值」更可靠,避免因字段过多导致的版本不匹配。
5. 验证子窗口前后的Current行是否一致
偶尔子窗口以ShowDialog()打开时,主窗体可能因失去焦点触发BindingNavigator的位置变化(比如不小心滚动了分页),导致你后续修改的不是原来的行。可以在子窗口打开前后记录procBindingSource.Position或者当前行的ID值,确认返回后没有变化。
内容的提问来源于stack exchange,提问作者Missy

