SqlDataAdapter.Update не записывает в таблицу до второго обновления

У меня есть программа, разработанная в С# 2010 Express с формой, состоящей из DataGridView с отдельными полями редактирования ячеек под ним. Когда строка выбрана (нажата в ее левом конце), значения в строке копируются в поля редактирования. Вносятся изменения и нажимается кнопка «Запомнить транзакцию». Новое значение появится в DataGridView. Если затем форма закрывается и снова открывается (удаляется и перезагружается), отображается исходное значение, а не новое значение. Очевидно, что запись в базе данных не была обновлена.

Если описанный выше процесс повторяется, за исключением того, что перед закрытием формы снова выбирается запись и снова нажимается кнопка «Запомнить транзакцию», значение сохраняется в базе данных. Когда форма закрывается, а затем снова открывается, появляется новое значение.

Очевидно, что желаемое поведение заключается в том, чтобы значение сохранялось в базе данных сразу после первого нажатия кнопки «Запомнить транзакцию».

В верхней части класса формы появляется следующее объявление:

    DataTable _dt;
    SqlDataAdapter _adapter;

Метод load_DataGridView() выглядит следующим образом. Обратите внимание, что запросы адаптера на обновление, удаление и вставку создаются с вызовами соответствующих методов GetXXXCommand() класса SqlCommandBuilder. Точки останова во время отладки указывают на то, что они были правильно заполнены.

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

Код, который вызывается при нажатии кнопки «Запомнить транзакцию», выглядит следующим образом:

    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
    }

Помощь! Это сводит меня с ума!


person Jim I.    schedule 02.08.2014    source источник
comment
Я бы предположил, что вы идете по этому пути неправильно. Вы должны заполнить DataTable, связать это с BindingSource, а затем связать это с DataGridView И вашими полями редактирования. Когда пользователь выбирает строку в сетке, ее поля автоматически отображаются в полях редактирования; код не требуется. Любые изменения, сделанные в полях редактирования, будут автоматически возвращены в сетку после фиксации обновления. Это произойдет автоматически, когда вы перейдете к другой записи, но вы также можете вызвать EndEdit на BindingSource, чтобы принудительно выполнить ее перед сохранением.   -  person jmcilhinney    schedule 02.08.2014
comment
Имеет смысл - я полностью за отсутствие кода. Но я не уверен, как выглядит код для привязки полей редактирования (всех 24) к BindingSource. Не могли бы вы привести короткий пример? ТИА   -  person Jim I.    schedule 02.08.2014
comment
Кроме того, я не заполняю DataTable, когда говорю _adapter.Fill(_dt); и запомнил DataGridView.DataSource = _dt;? (вам нужно прокрутить окно кода вниз, чтобы увидеть эти строки)   -  person Jim I.    schedule 02.08.2014
comment
Да, этот вызов Fill — это то, как вы заполняете DataTable, но я хотел включить все необходимые шаги в свое описание. Я опубликую пример кода в качестве ответа.   -  person jmcilhinney    schedule 02.08.2014
comment
Спасибо, Джей, дайте мне день, чтобы попробовать это, и я опубликую результаты и отмечу их как решение. Гораздо яснее.   -  person Jim I.    schedule 02.08.2014


Ответы (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);
}
person jmcilhinney    schedule 02.08.2014
comment
Еще раз спасибо, j, как только я сделал необходимые преобразования в C# (не такая уж большая проблема, к счастью, VB и C# довольно близки в этом случае, но могут сбить с толку новичков), все заработало отлично. У меня есть эта бронза для дальнейшего использования! :-) - person Jim I.; 02.08.2014
comment
Ужасно сожалею о размещении кода VB. Я в основном отвечал на вопросы VB и забыл, что на этот раз не был. Даже если вы уже сделали это для себя, я заменю код на случай, если кто-то еще задаст этот вопрос с похожей проблемой. - person jmcilhinney; 02.08.2014