1

编辑:无论您是否认为这是一个 .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
4

1 回答 1

2

这不是错误,关键是您以(非常)非标准方式使用 ID。两个答案:

1)使用 DataTable.GetChanges(DataRowState.Modified) 按顺序处理您的更新(我认为它会被删除、修改、插入)。这也是您对主/详细关系关系所做的事情(在 .net 3.0 之前)

2) 重新考虑您的设计,一般来说,ID 应该是不可变的,并允许存在间隙等。这将使您的所有数据库操作更加可靠和容易。您可以使用另一列来维护顺序编号以呈现给用户。

于 2009-06-22T15:59:36.160 回答