SqlDataAdapter.Update 直到第二次更新才写入表
Posted
技术标签:
【中文标题】SqlDataAdapter.Update 直到第二次更新才写入表【英文标题】:SqlDataAdapter.Update not writing to table until second update 【发布时间】:2014-08-02 01:15:03 【问题描述】:我有一个用 C# 2010 Express 开发的程序,它的表单由一个 DataGridView 组成,下面有一个单独的单元格编辑字段。选择行(在其左端单击)时,行中的值将复制到编辑字段中。进行了更改,并单击了“记忆交易”按钮。新值出现在 DataGridView 中。如果表单随后关闭并重新打开(处理并重新加载),则显示原始值,而不是新值。显然数据库记录没有更新。
如果重复上述过程,除了在关闭表单之前再次选择记录并再次单击“记忆事务”按钮外,该值将存储在数据库中。当表单关闭然后重新打开时,会出现新值。
显然,所需的行为是在第一次单击“记忆交易”按钮时立即将值存储在数据库中。
以下声明出现在表单类的顶部:
DataTable _dt;
SqlDataAdapter _adapter;
方法 load_DataGridView() 如下所示。请注意,适配器的更新、删除和插入查询是通过调用它们各自的 SqlCommandBuilder 的 GetXXXCommand() 方法构建的。调试期间的断点表明这些已正确填充。
private void load_dataGridView()
DataTable dt = new DataTable();
_dt = dt;
SqlConnection dbconn = new SqlConnection(dbq.connectionString);
dbconn.Open();
string qstr = @"SELECT transaction_id
, 0 as pay
, m.has_splits
, m.Num
, m.Type
, m.last_date
, m.next_date
, m.Payee
, m.Memo
, m.Tax_Category
, m.Amount
, m.Clr
, m.Initials
, m.sales_tax_paid
, m.from_institution_id as FromInstitutionID
, m.from_institution as FromInstitution
, m.from_account_id as FromAccountID
, m.from_account as FromAccount
, m.from_envelope_id as FromEnvelopeID
, m.from_envelope as FromEnvelope
, m.to_institution_id as ToInstitutionID
, m.to_institution as ToInstitution
, m.to_account_id as ToAccountID
, m.to_account as ToAccount
, m.to_envelope_id as ToEnvelopeID
, m.to_envelope as ToEnvelope
, m.frequency
FROM memorized m
";
SqlDataAdapter adapter = new SqlDataAdapter(qstr,dbconn);
SqlCommandBuilder scb = new SqlCommandBuilder(adapter);
adapter.UpdateCommand = new SqlCommandBuilder(adapter).GetUpdateCommand();
adapter.DeleteCommand = new SqlCommandBuilder(adapter).GetDeleteCommand();
adapter.InsertCommand = new SqlCommandBuilder(adapter).GetInsertCommand();
_adapter = adapter;
_adapter.Fill(_dt);
build_dgv_columns(Properties.Settings.Default.currentInstitutionId,
Properties.Settings.Default.currentAccountId);
memorizedDataGridView.DataSource = _dt;
点击Memorize Transaction按钮时调用的代码如下:
private void bnMemorizeTx_Click(object sender, EventArgs e)
if (_newTx)
_currentTxRow = _dt.Rows.Count;
_dt.Rows.Add();
_newTx = false;
else
_currentTxRow = memorizedDataGridView.SelectedRows[0].Index;
populateRowFromFields(memorizedDataGridView.Rows[_currentTxRow]);
memorizedDataGridView.Refresh();
memorizedDataGridView.ClearSelection();
//grid shows new value with no rows selected at this point, row is "dirty"
try
_adapter.Update(_dt);
catch (SqlException e1)
MessageBox.Show(e1.Message + e1.InnerException, "Error Thrown",MessageBoxButtons.OK);
reset_transaction_fields();
从来没有抛出异常。我怀疑将数据从 DataGridView 拉到编辑字段中,然后以某种方式再次将其写回的操作会以某种方式使用 _adapter.Update(_dt) 命令检测行已更改的能力,因此当它被调用时,它不会不更新任何东西。然后在第二次尝试时,当重新选择行(显示新值)但没有进行编辑更改时,它确实有效。不知何故,即使新值出现在网格中,也好像尚未提交更改,必须重新选择该行才能发生这种情况。
populateRowFromFields 方法如下,使用单元格索引的列名引用将编辑字段中的值写回行中的单元格值:
private void populateRowFromFields(DataGridViewRow dgvr)
dgvr.Cells["Num"].Value = tbRefNum.Text;
dgvr.Cells["Type"].Value = cbType.GetItemText(cbType.SelectedItem);
dgvr.Cells["last_date"].Value = dtpLastPaid.Value.ToShortDateString();
dgvr.Cells["next_date"].Value = dtpNextPay.Value.ToShortDateString();
dgvr.Cells["Payee"].Value = tbPayee.Text;
dgvr.Cells["Amount"].Value = tbAmount.Text;
dgvr.Cells["FromInstitutionID"].Value = cbFromInst.GetItemText(cbFromInst.SelectedValue); // fromInst
dgvr.Cells["FromInstitution"].Value = cbFromInst.GetItemText(cbFromInst.SelectedItem); // fromAcct
dgvr.Cells["FromAccountID"].Value = cbFromAcct.GetItemText(cbFromAcct.SelectedValue); // fromEnv
dgvr.Cells["FromAccount"].Value = cbFromAcct.GetItemText(cbFromAcct.SelectedItem); // fromInst
dgvr.Cells["FromEnvelopeID"].Value = cbFromEnv.GetItemText(cbFromEnv.SelectedValue); // fromAcct
dgvr.Cells["FromEnvelope"].Value = cbFromEnv.GetItemText(cbFromEnv.SelectedItem); // fromEnv
if ((cbType.GetItemText(cbType.SelectedItem) == "+iTx") || (cbType.GetItemText(cbType.SelectedItem) == "-iTx") || (cbType.GetItemText(cbType.SelectedItem) == "+eTx") || (cbType.GetItemText(cbType.SelectedItem) == "-eTx"))
dgvr.Cells["ToInstitutionID"].Value = cbToInst.GetItemText(cbToInst.SelectedValue); // toInst
dgvr.Cells["ToInstitution"].Value = cbToInst.GetItemText(cbToInst.SelectedItem); // toInst
dgvr.Cells["ToAccountID"].Value = cbToAcct.GetItemText(cbToAcct.SelectedValue); // toAcct
dgvr.Cells["ToAccount"].Value = cbToAcct.GetItemText(cbToAcct.SelectedItem); // toAcct
dgvr.Cells["ToEnvelopeID"].Value = cbToEnv.GetItemText(cbToEnv.SelectedValue); // toEnv
dgvr.Cells["ToEnvelope"].Value = cbToEnv.GetItemText(cbToEnv.SelectedItem); // toEnv
hideTxToFields();
dgvr.Cells["Memo"].Value = tbMemo.Text;
dgvr.Cells["Tax_Category"].Value = cbTaxCategory.GetItemText(cbTaxCategory.SelectedItem); // taxCategory
dgvr.Cells["sales_tax_paid"].Value = cbSalesTaxPaid.GetItemText(cbSalesTaxPaid.SelectedItem); // salesTaxPaid
dgvr.Cells["Clr"].Value = "U";
dgvr.Cells["Initials"].Value = cbBy.GetItemText(cbBy.SelectedItem); // by
dgvr.Cells["frequency"].Value = cbFrequency.GetItemText(cbFrequency.SelectedItem); // frequency
帮助!这让我发疯了!
【问题讨论】:
我建议您以错误的方式解决此问题。您应该填充DataTable
,将其绑定到BindingSource
,然后将其绑定到您的DataGridView
和您的编辑字段。当用户在网格中选择一行时,其字段将自动显示在编辑字段中;无需代码。提交更新时,在编辑字段中所做的任何更改都将自动推送回网格。当您导航到另一条记录时,这将自动发生,但您也可以在保存之前调用 BindingSource
上的 EndEdit
强制它。
有道理——我完全赞成“不需要代码”。但我不确定将编辑字段(全部 24 个)绑定到 BindingSource 的代码是什么样的。你能举一个简短的例子吗? TIA
另外,当我说 _adapter.Fill(_dt); 时,我不是在填充数据表吗?和 memorizedDataGridView.DataSource = _dt;? (您必须在代码窗口中向下滚动才能看到这些行)
是的,Fill
调用是您填充DataTable
的方式,但我想在我的描述中包含所有必需的步骤。我将发布一个代码示例作为答案。
谢谢,j,给我一天的时间试试这个,然后我会发布结果并标记为解决方案。更清晰。
【参考方案1】:
最简单的选择是通过BindingSource
对网格和编辑控件使用数据绑定,例如
private SqlConnection connection;
private SqlDataAdapter adapter;
private SqlCommandBuilder builder;
private DataTable table;
private void Form1_Load(object sender, EventArgs e)
connection = new SqlConnection("connection string here");
adapter = new SqlDataAdapter("SELECT * FROM Person", connection);
builder = new SqlCommandBuilder(adapter);
table = new DataTable();
adapter.Fill(table);
bindingSource1.DataSource = table;
dataGridView1.DataSource = bindingSource1;
givenNameTextBox.DataBindings.Add("Text", bindingSource1, "GivenName");
familyNameTextBox.DataBindings.Add("Text", bindingSource1, "FamilyName");
private void addButton_Click(object sender, EventArgs e)
bindingSource1.AddNew();
private void deleteButton_Click(object sender, EventArgs e)
bindingSource1.RemoveCurrent();
private void saveButton_Click(object sender, EventArgs e)
bindingSource1.EndEdit();
adapter.Update(table);
【讨论】:
再次感谢,j,一旦我对 C# 进行了必要的转换(没什么大不了的,谢天谢地,VB 和 C# 在这种情况下非常接近,但可能会让新手感到困惑),它工作得很好.我有这个古铜色以备将来参考! :-) 非常抱歉发布 VB 代码。我大部分时间都在回答 VB 问题,却忘了这次我不是。即使您已经为自己完成了,我也会替换代码以防其他人遇到类似问题。以上是关于SqlDataAdapter.Update 直到第二次更新才写入表的主要内容,如果未能解决你的问题,请参考以下文章
SqlDataAdapter.update() 不更新数据库
在 SqlDataAdapter.Update() 中获取错误消息
200万条记录的SqlDataAdapter.Update()速度极慢