同步数据表自增列与数据库

本文关键字:数据库 数据表 同步 | 更新日期: 2023-09-27 18:17:23

DBConcurrencyException问题发生时,试图在我的数据库上使用Update()方法。我在数据库中有一个表,它有一个自动递增的ID列,在我的c#程序中有一个数据表,它从这个表中获取信息(包括当我使用MissingSchemaAction = MissingSchemaAction. addwithkey时的自动递增部分)。

如果我创建行并将它们添加到数据表中,数据表会自动为我填充自动递增的ID列(从数据库表停止的地方开始),这很好。但是,如果我删除刚刚添加的行(没有首先使用Update())并添加新的行,则数据表自动增量列将根据datatable所在的位置填充值,而不是数据库所在的位置,这就是为什么我得到并发性错误。

例如:

数据库中的表有以下记录:

1 Apple
2 Orange
3 Pear

它被复制到数据表中,所以当我添加一个名为"grape"的新行时,我得到:

1 Apple
2 Orange
3 Pear
4 Grape

这很好,但是如果不运行Update()方法,我删除葡萄行并添加新行"Melon",我得到:

1 Apple
2 Orange
3 Pear
5 Melon

当我尝试运行Update()时,数据库期望4是下一个自动递增的值,而不是得到5。我得到了误差。Update()在用户单击"保存"按钮时发生,因此理想情况下,我希望他们能够在最后保存之前进行如上所示的大量更改,但是在每一行添加/删除后使用Update()是保持并发性的唯一方法吗?

同步数据表自增列与数据库

期望值是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)

我的第一个想法是,你应该只是处理的情况下,一行被删除,首先更新它,然后删除它,以保持自动增量id同步。

然而,我遇到了同样的情况,它似乎是由第三方控制我的DataGridView托管。具体来说,当用户将焦点放在DataGridView的"新"行上,切换到另一个应用程序,然后点击回到DataGridView时,就会出现问题。此时,将删除新行的原始DataRow实例,并创建一个具有递增ID值的新实例。我还没有能够找出一种方法来处理该行的删除之前,它被实际删除,我也不能弄清楚什么第三方控制正在做触发这。

因此,目前我正在以一种非常笨拙的方式处理这个问题,通过从数据库中查询正确的自动增量值,并在必要时更正新的datarow。如果其他方法都失败了,这个解决方案似乎是有效的。(注意我使用的是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;
                }
            }
        }
    }
}