编辑:无论您是否认为这是一个 .NET 错误,任何评论都将不胜感激。
我有一个错误,我设法将其简化为以下场景:
我有一个 DataTable,其中主键必须保持连续,例如,如果您在其他行之间插入一行,则必须首先增加后续行的 ID 以腾出空间,然后插入该行。
如果删除一行,则必须递减任何后续行的 ID 以填补表中该行留下的空白。
正常工作的测试用例
从表中的 3 行开始,ID 为 1、2 和 3。
然后删除ID=2,设置ID=2 where ID=3(填补空白);这工作正常。dataTable.GetChanges() 包含删除的行,然后是修改的行;当您运行 dataAdapter.Update(table) 时,它执行得很好。
不起作用的测试用例
但是,如果您从 2 行(ID 1 和 2)开始,则在 ID=2 的地方设置 ID=3,并插入 ID=2,然后提交(或接受)更改。这现在应该与第一次测试的状态相同。
然后您执行与之前相同的步骤,即删除 ID=2 并在 ID=3 的位置设置 ID=2,但现在 dataTable.GetChanges() 的顺序错误。第一行是修改的行,第二行是删除的行。然后,如果您尝试 dataAdapter.Update(table) ,它将违反主键 - 它尝试在删除之前将行修改为已经存在的行。
解决方法
我可以想到一个解决问题的方法,即强制它以便首先提交已删除的行,然后修改行,然后添加行。但是为什么会这样呢?还有其他解决方案吗?
我想我之前在字典中看到过类似的“问题”,如果你添加一些项目,然后删除,重新插入它们,那么它们将不会与你添加它们的顺序相同(当你枚举字典时)。
以下是两个显示问题的 NUnit 测试:
[Test]
public void GetChanges_Working()
{
// Setup ID table with three rows, ID=1, ID=2, ID=3
DataTable idTable = new DataTable();
idTable.Columns.Add("ID", typeof(int));
idTable.PrimaryKey = new DataColumn[] { idTable.Columns["ID"] };
idTable.Rows.Add(1);
idTable.Rows.Add(2);
idTable.Rows.Add(3);
idTable.AcceptChanges();
// Delete ID=2, and move old ID=3 to ID=2
idTable.Select("ID = 2")[0].Delete();
idTable.Select("ID = 3")[0]["ID"] = 2;
// Debug GetChanges
foreach (DataRow row in idTable.GetChanges().Rows)
{
if (row.RowState == DataRowState.Deleted)
Console.WriteLine("Deleted: {0}", row["ID", DataRowVersion.Original]);
else
Console.WriteLine("Modified: {0} = {1}", row["ID", DataRowVersion.Original], row["ID", DataRowVersion.Current]);
}
// Check GetChanges
Assert.AreEqual(DataRowState.Deleted, idTable.GetChanges().Rows[0].RowState, "1st row in GetChanges should be deleted row");
Assert.AreEqual(DataRowState.Modified, idTable.GetChanges().Rows[1].RowState, "2nd row in GetChanges should be modified row");
}
输出:
Deleted: 2
Modified: 3 = 2
1 passed, 0 failed, 0 skipped, took 4.27 seconds (NUnit 2.4).
下一个测试:
[Test]
public void GetChanges_NotWorking()
{
// Setup ID table with two rows, ID=1, ID=2
DataTable idTable = new DataTable();
idTable.Columns.Add("ID", typeof(int));
idTable.PrimaryKey = new DataColumn[] { idTable.Columns["ID"] };
idTable.Rows.Add(1);
idTable.Rows.Add(2);
idTable.AcceptChanges();
// Move old ID=2 to ID=3, and add ID=2
idTable.Select("ID = 2")[0]["ID"] = 3;
idTable.Rows.Add(2);
idTable.AcceptChanges();
// Delete ID=2, and move old ID=3 to ID=2
idTable.Select("ID = 2")[0].Delete();
idTable.Select("ID = 3")[0]["ID"] = 2;
// Debug GetChanges
foreach (DataRow row in idTable.GetChanges().Rows)
{
if (row.RowState == DataRowState.Deleted)
Console.WriteLine("Deleted: {0}", row["ID", DataRowVersion.Original]);
else
Console.WriteLine("Modified: {0} = {1}", row["ID", DataRowVersion.Original], row["ID", DataRowVersion.Current]);
}
// Check GetChanges
Assert.AreEqual(DataRowState.Deleted, idTable.GetChanges().Rows[0].RowState, "1st row in GetChanges should be deleted row");
Assert.AreEqual(DataRowState.Modified, idTable.GetChanges().Rows[1].RowState, "2nd row in GetChanges should be modified row");
}
输出:
Modified: 3 = 2
Deleted: 2
TestCase 'GetChanges_NotWorking'
failed:
1st row in GetChanges should be deleted row
Expected: Deleted
But was: Modified