尝试在我的数据库上使用Update()方法时发生DBConcurrencyException问题。我在数据库中有一个表,它有一个自动增加的ID列,以及我的c#程序中的一个DataTable,它从该表中获取信息(包括当我使用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的唯一方法( )添加/删除每一行后?
答案 0 :(得分:0)
预期值为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)
答案 1 :(得分:0)
我首先想到的是你应该只处理删除行并首先更新它的情况,然后删除它以保持自动增量ID同步。
但是,我遇到了同样的情况,它似乎是由我的DataGridView托管的第三方控件引起的。具体来说,当用户将焦点放在DataGridView的“new”行中时会出现问题,交换机到另一个应用程序,然后单击回DataGridView。此时,将删除新行的原始DataRow实例,并使用递增的ID值创建新的实例。在实际删除行之前,我无法找到处理删除行的方法,也无法弄清楚第三方控件正在做什么来触发它。
因此,目前我正以非常严厉的方式处理此问题,通过从数据库查询正确的自动增量值,必要时更正新的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;
}
}
}
}
}