Синхронизация автоматически обновляемых столбцов DataTable с базой данных - PullRequest
3 голосов
/ 08 декабря 2011

Проблема DBConcurrencyException возникает при попытке использовать метод Update () в моей базе данных.У меня есть таблица в базе данных, в которой есть столбец идентификатора с автоинкрементом, и таблица данных в моей программе на c #, которая получает информацию из этой таблицы (включая часть с автоинкрементом, когда я использую MissingSchemaAction = MissingSchemaAction.AddWithKey).

Если я создаю строки и добавляю их в таблицу данных, таблица данных автоматически заполняет для меня столбец идентификатора с автоинкрементом (начиная с того места, где остановилась таблица базы данных), и это нормально.Однако, если я удаляю только что добавленные строки (без предварительного использования Update ()) и добавляю новые, столбец автоинкремента с датой заполняется значением, основанным на том, где находится DATATABLE, а не на том, где находится база данных, поэтому я получаюошибка параллелизма.

например:

Таблица в базе данных содержит следующие записи:

1 Apple
2 Orange
3 Pear

, которая копируется в таблицу данных, поэтому при добавлении новой строкисо значением name "grape" я получаю:

1 Apple
2 Orange
3 Pear
4 Grape

Это нормально, однако, если не запускается метод Update (), я удаляю строку с виноградом и добавляю новую строку "Melon". Iget:

1 Apple
2 Orange
3 Pear
5 Melon

И когда я пытаюсь запустить Update (), база данных ожидает, что 4 будет следующим автоинкрементным значением, но вместо этого получит 5. Так что я получаю ошибку.Update () происходит, когда пользователь нажимает кнопку «сохранить», поэтому в идеале я бы хотел, чтобы они могли внести множество изменений, как показано выше, перед окончательным сохранением, но это единственный способ сохранить параллелизм для использования Update () после добавления / удаления каждой строки?

Ответы [ 2 ]

0 голосов
/ 29 июля 2013

Моей первой мыслью было бы то, что вы должны просто обработать случай удаления строки и сначала обновить ее, а затем удалить, чтобы синхронизировать идентификаторы автоинкремента.

Тем не менее, я столкнулся с той же ситуацией, и это, кажется, вызвано сторонним контролем, в котором мой DataGridView находится. в другое приложение, а затем щелкает обратно в DataGridView. На этом этапе исходный экземпляр DataRow для новой строки удаляется, и создается новый с увеличенным значением идентификатора. Мне не удалось выяснить способ обработки удаления строки до ее фактического удаления, и я не могу выяснить, что делает сторонний элемент управления, вызывающий это.

Поэтому, на данный момент, я решаю эту проблему очень жестким способом, запрашивая правильное значение автоинкремента из базы данных, исправляя новые DataRows, если это необходимо. Если все остальное терпит неудачу, это решение, кажется, работает. (Обратите внимание, что я использую SqlCe вместо MySQL)

void OnLoad()
{
    base.OnLoad(e);
    ...
    _dataTable.TableNewRow += HandleTableNewRow;
}

void HandleTableNewRow(object sender, DataTableNewRowEventArgs e)
{
    SetAutoIncrementValues(e.Row);
}

void SetAutoIncrementValues(DataRow row)
{
    foreach (DataColumn dataColumn in _dataTable.Columns
        .OfType<DataColumn>()
        .Where(column => column.AutoIncrement))
    {
        using (SqlCeCommand sqlcmd = new SqlCeCommand(
            "SELECT AUTOINC_NEXT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" +
            Name + "' AND COLUMN_NAME = '" + dataColumn.ColumnName + "'", _connection))
        using (SqlCeResultSet queryResult =
            sqlcmd.ExecuteResultSet(ResultSetOptions.Scrollable))
        {
            if (queryResult.ReadFirst())
            {
                var nextValue = Convert.ChangeType(queryResult.GetValue(0), dataColumn.DataType);

                if (!nextValue.Equals(row[dataColumn.Ordinal]))
                {
                    // Since an auto-increment column is going to be read-only, apply
                    // the new auto-increment value via a separate array variable.
                    object[] rowData = row.ItemArray;
                    rowData[dataColumn.Ordinal] = nextValue;
                    row.ItemArray = rowData;
                }
            }
        }
    }
}
0 голосов
/ 08 декабря 2011

Ожидаемое значение равно 5 - для базы данных было бы безумно неэффективно пытаться заполнить отверстия в столбце каждый раз, когда вы что-то делаете. Как только auto_increment используется, он исчезает навсегда.

Из-за этого всегда проверяйте, достаточно ли велика ваша колонка, в которой хранятся все ваши записи. Например, если вы использовали TINYINT, в вашей таблице может быть только 127 записей.

Автоинкремент сохраняется на уровне таблицы, и Mysql никогда не оглядывается, чтобы увидеть, может ли оно быть ниже. Вы можете вручную изменить его, выполнив следующие действия:

ALTER TABLE tablename AUTO_INCREMENT=2;

Но если вы сделаете это, и в будущем произойдет столкновение - произойдут плохие вещи.

Или вы можете проверить, что это такое

SHOW CREATE TABLE tablename;
CREATE TABLE `tablename` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`cat_id` int(10) unsigned NOT NULL,
`status` int(10) unsigned NOT NULL,
`date_added` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `categories_list_INX` (`cat_id`,`status`),
KEY `cat_list_INX` (`date_added`,`cat_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1

И ты узнаешь, какой последний.

SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...