Ошибка добавления записей в дочернюю таблицу C# + Access
вопрос наверняка решается очень просто, но никак не пойму в чем дело... Проблема такая: Есть БД Access содержащая две связанные 1:М таблицы Таб1 и Таб2, в приложении WindowsForms добавляю новый источник данных... на форму кидаю два грида, первый отображает Таб1, второй связь между Таб1 и Таб2. Во время исполнения записи добавляются, однако при попытке сохранить новые записи выходит ошибка "Невозможно добавление или изменение записи. Для обеспечения целостности данных необходимо наличие связанной записи в таблице имя_таблицы". В программе никакого кода кроме Таб1TableAdapter.Update(имя_датасета) и Таб2TableAdapter.Update(имя_датасета) нет. В метод пособии прочитал что этого должно быть достаточно... Подскажите в чем моя ошибка?
Заранее спасибо!
PS
Руки не кривые и голова на месте, не откажусь от ссылки на толковую литературу по работе с БД. На C# пересел недавно, раньше писал в Delphi, поэтому пока плаваю в некоторых вопросах.
В ADO.NET необходимо не только сказать адаптеру "обновись", но и сказать ему КАК это сделать.
К счастью для структурированных таблиц ( в которых есть закрепленные ключи ) команду обновление можно получить программно.
Фрагмент соединения с БД и получения команд обновления\вставки\удаления
Код:
OleDbConnection conn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + textBox_DBpath.Text);
conn.Open();
TableView.dbAdapter = new OleDbDataAdapter("SELECT * FROM " + TableView.listViewItem.Text, conn);
OleDbCommandBuilder ComBilder = new OleDbCommandBuilder(TableView.dbAdapter);
try
{
TableView.dbAdapter.UpdateCommand = ComBilder.GetUpdateCommand();
TableView.dbAdapter.InsertCommand = ComBilder.GetInsertCommand();
TableView.dbAdapter.DeleteCommand = ComBilder.GetDeleteCommand();
}
catch (InvalidOperationException ioe)
{
MessageBox.Show("Доступ только для чтения\n\r" + ioe.Message, "Доступен только для чтения", MessageBoxButtons.OK, MessageBoxIcon.Warning);
TableView.dataGridView.ReadOnly = true;
TableView.Text += " ReadOnly(!!!)";
}
conn.Close();
conn.Open();
TableView.dbAdapter = new OleDbDataAdapter("SELECT * FROM " + TableView.listViewItem.Text, conn);
OleDbCommandBuilder ComBilder = new OleDbCommandBuilder(TableView.dbAdapter);
try
{
TableView.dbAdapter.UpdateCommand = ComBilder.GetUpdateCommand();
TableView.dbAdapter.InsertCommand = ComBilder.GetInsertCommand();
TableView.dbAdapter.DeleteCommand = ComBilder.GetDeleteCommand();
}
catch (InvalidOperationException ioe)
{
MessageBox.Show("Доступ только для чтения\n\r" + ioe.Message, "Доступен только для чтения", MessageBoxButtons.OK, MessageBoxIcon.Warning);
TableView.dataGridView.ReadOnly = true;
TableView.Text += " ReadOnly(!!!)";
}
conn.Close();
И теперь само обновление по нажатию save'ика
Код:
private void toolSaveChanged_Click(object sender, EventArgs e)
{
this.dataGridView.CancelEdit();
this.dataGridView.ClearSelection();
try
{
this.dbAdapter.Update(this.dataGridView.DataSource as DataTable);
(this.dataGridView.DataSource as DataTable).AcceptChanges();
}
catch (InvalidOperationException ioe)
{
MessageBox.Show("Ошибка обновления данных базы\n\r" + ioe.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
{
this.dataGridView.CancelEdit();
this.dataGridView.ClearSelection();
try
{
this.dbAdapter.Update(this.dataGridView.DataSource as DataTable);
(this.dataGridView.DataSource as DataTable).AcceptChanges();
}
catch (InvalidOperationException ioe)
{
MessageBox.Show("Ошибка обновления данных базы\n\r" + ioe.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Могу скинуть полный код ( прикрепить к мессейджу не удалось - дюже большой ) - так что если понадобиться - напишите в личку.
Решение нашел случайно, просто стал изучать схему данных и обнаружил что шаг инкремента -1, не знаю почему так сделано по умолчанию, но наверно у разработчиков были веские аргументы для этого.
После исправления данные стали сохраняться, для этого достаточно всего двух строк теста вида:
TabAdapter.Update(Главная_Таблица);
TabAdapter.Update(Подчиненная_Таблица);
PS
Спасибо, за то что откликнулись!
Тема закрыта
live22h не зря заметил, что у разработчиков были веские аргументы, что бы по умолчанию использовать инкремент с шагом -1. Дело в том что это, так называемые, фиктивные индексы. По умолчанию они не передаются в базу данных, так как база сама генерирует индексы ключа. И что бы не вводить в заблуждение пользователей обычно их делают отрицательными.
Обычно инкремент индекса в базе данных происходит с шагом +1, поэтому решение проблемы, предложенное live22h, в какой то степени, верное. Фактически мы делаем шаг инкремента в нашем датасет таким же как и в базе. Но все таки это не лучшее решение.
Приведу пример. Допустим у нас есть 2 связанные таблицы (родительская с первичным ключом, и дочерняя). Добавим строку в родительскую таблицу, пусть там уже было 10 записей, поэтому значение ключа у новой строки будет 11. Затем добавим строку в дочернюю таблицу, у нее есть поле которое ссылается на индекс строки из родительской таблицы: 11.
Затем сделаем это еще раз. Еще одну строку в родительскую - значение ключа станет 12, и еще одну строку в дочернюю таблицу с ссылкой на строку родительской таблицы с индексом 12. А теперь удалим первые добавленные строки из таблиц, а уже потом попробуем передать новые данные в базу.
Возникнет ошибка, база добавит новую строку в родительскую таблицу и установит значение ключа равным 11. Однако в дочерней таблице мы все еще ссылаемся на строку с индексом 12.
А теперь вопрос. Как передать правильные значения ключа в дочернюю таблицу, желательно что бы обновление данных происходило за один раз.