同步数据表自增列与数据库
本文关键字:数据库 数据表 同步 | 更新日期: 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;
}
}
}
}
}