【问题标题】:Is this a bug in DataTable API? Changes are stored/executed in the "wrong sequence"这是 DataTable API 中的错误吗?更改以“错误的顺序”存储/执行
【发布时间】:2010-11-04 20:29:52
【问题描述】:

编辑:无论您是否认为这是一个 .NET 错误,任何 cmets 都将不胜感激。

我有一个错误,我设法将其简化为以下场景:

我有一个 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

【问题讨论】:

    标签: c# datatable dataadapter


    【解决方案1】:

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

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

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

    【讨论】:

    • +1 表示“一般 ID 应该是不可变的”和“您可以使用另一列来维护顺序编号以呈现给用户”。
    • 我明白你的观点,主键不应该以这种方式使用;但对我来说,DataTable 似乎仍然很奇怪——也就是说,如果我以这种方式使用它,它应该可以工作。相关,为什么 DataAdapter.Update 的默认行为不是按删除、修改然后插入的顺序进行处理。 (顺便说一句,这是我打算在我的解决方法中输入的顺序 - 我现在已经在原文中更正了它)。
    • @Rick:适配器具有“有限”功能(即无法处理关系),所以如果你想要一些非标准的东西,你必须自己处理。
    • 好的,谢谢,我等着看有没有人回答,否则我会接受你的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多