编辑:您是否认为这是一个.NET错误的任何评论将不胜感激。
我有一个错误,我已经设法简化到以下场景:
我有一个DataTable,其中主键必须保持连续,例如如果在其他行之间插入一行,则必须先增加后续行的ID以腾出空格,然后插入该行。
如果删除一行,则必须递减任何后续行的ID以填充表中行所留下的间隙。
正常运行的测试用例
从表格中的3行开始,ID为1,2和3。
然后删除ID = 2,并设置ID = 2,其中ID = 3(填补空白);这工作正常。 dataTable.GetChanges()包含已删除的行,然后是已修改的行;当你运行dataAdapter.Update(table)时,它执行正常。
不起作用的测试用例
但是,如果从2行(ID 1和2)开始,则设置ID = 3,其中ID = 2,并插入ID = 2,然后提交(或接受)更改。现在应该与第一次测试的状态相同。
然后你执行与以前相同的步骤,即删除ID = 2并设置ID = 2,其中ID = 3,但现在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
答案 0 :(得分:2)
这不是一个错误,关键是你以(非常)非标准的方式使用ID。两个答案:
1)使用DataTable.GetChanges(DataRowState.Modified)按顺序处理您的更新(我认为它将被删除,修改,插入)。这也是您与主/细节关系(在.net 3.0之前)
的关系2)重新考虑你的设计,一般来说ID应该是不可变的,并允许间隙等。这将使你的所有数据库操作更加可靠和容易。您可以使用另一列来维护序列编号以呈现给用户。