Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/.htaccess/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 这是DataTableAPI中的错误吗?更改存储/执行在";“顺序错误”;_C#_Datatable_Dataadapter - Fatal编程技术网

C# 这是DataTableAPI中的错误吗?更改存储/执行在";“顺序错误”;

C# 这是DataTableAPI中的错误吗?更改存储/执行在";“顺序错误”;,c#,datatable,dataadapter,C#,Datatable,Dataadapter,编辑:无论您是否认为这是一个.NET错误,任何评论都将不胜感激 我有一个bug,我设法将其简化为以下场景: 我有一个数据表,其中主键必须保持连续,例如,如果在其他行之间插入一行,则必须首先增加后续行的ID以留出空间,然后插入该行 如果删除一行,则必须减少任何后续行的ID,以填补表中该行留下的空白 正常工作的测试用例 从表中的3行开始,ID为1、2和3 然后删除ID=2,设置ID=2,其中ID=3(以填补空白);这是正确的。GetChanges()包含删除的行,然后是修改的行;当您运行dataAd

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

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

我有一个数据表,其中主键必须保持连续,例如,如果在其他行之间插入一行,则必须首先增加后续行的ID以留出空间,然后插入该行

如果删除一行,则必须减少任何后续行的ID,以填补表中该行留下的空白

正常工作的测试用例

从表中的3行开始,ID为1、2和3

然后删除ID=2,设置ID=2,其中ID=3(以填补空白);这是正确的。GetChanges()包含删除的行,然后是修改的行;当您运行dataAdapter.Update(表)时,它会正常执行

不起作用的测试用例

但是,如果从两行(ID1和ID2)开始,然后设置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).
Modified: 3 = 2
Deleted: 2
TestCase 'GetChanges_NotWorking'
failed: 
  1st row in GetChanges should be deleted row
  Expected: Deleted
  But was:  Modified
下一个测试:

[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");
}
输出:

Deleted: 2
Modified: 3 = 2

1 passed, 0 failed, 0 skipped, took 4.27 seconds (NUnit 2.4).
Modified: 3 = 2
Deleted: 2
TestCase 'GetChanges_NotWorking'
failed: 
  1st row in GetChanges should be deleted row
  Expected: Deleted
  But was:  Modified

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

1) 使用DataTable.GetChanges(DataRowState.Modified)按顺序处理更新(我认为它会被删除、修改和插入)。这也是主/细节关系(在.NET3.0之前)必须做的事情


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

+1表示“一般情况下,ID应该是不可变的”和“您可以使用另一列来维护向用户显示的顺序编号”。我明白您的观点,即不应以这种方式使用主键;但对我来说,DataTable的行为似乎仍然很奇怪——也就是说,如果我这样使用它,它应该可以工作。相关的,为什么DataAdapter.Update的默认行为不是以删除、修改然后插入的顺序进行处理。(顺便说一句,这是我本想输入解决方案的顺序-我现在已经在原文中更正了)。@Rick:适配器的功能“有限”(即无法处理关系),因此如果你想要非标准的东西,你必须自己处理。好的,谢谢,我会等待其他人的回答,否则我会接受你的回答。