编辑:您是否认为这是一个.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
有帮助吗?

解决方案

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

1)使用DataTable.GetChanges(DataRowState.Modified)按顺序处理您的更新(我认为它将被删除,修改,插入)。这也是您与主/细节关系(在.net 3.0之前)

的关系

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top