編集: これが .NET のバグであるかどうかに関係なく、コメントをいただければ幸いです。
次のシナリオに単純化することができたバグがあります。
たとえば、他の行の間に行を挿入する場合、最初に後続の行の ID をインクリメントしてスペースを空けてから、行を挿入する必要があります。
また、行を削除する場合は、後続の行の ID をデクリメントして、テーブル内の行によって残されたギャップを埋める必要があります。
正しく動作するテスト ケース
ID が 1、2、3 のテーブル内の 3 つの行から始めます。
次に、ID=2 を削除し、ID=2 を 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() の順序が間違っています。最初の行は変更された行で、2 番目の行は削除された行です。次に、 dataAdapter.Update(table) を試すと、主キー違反が発生します-削除する前に、行を既存の行に変更しようとしました。
回避策
問題の回避策を考えることができます。つまり、削除された行が最初にコミットされ、次に行が変更され、次に行が追加されるように強制します。しかし、なぜこれが起こっているのですか?別の解決策はありますか?
以前に辞書で同様の「問題」を見たことがあると思います.いくつかのアイテムを追加してから削除し、それらを再挿入すると、それらは追加したのと同じ順序にはなりません(辞書を列挙するとき)。
問題を示す 2 つの 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