1

編集: これが .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
4

1 に答える 1

2

これはバグではありません。重要なのは、IDを(非常に)非標準的な方法で使用しているということです。2つの答え:

1)DataTable.GetChanges(DataRowState.Modified)を使用して、更新を順番に処理します(削除、変更、挿入されると思います)。これは、マスター/詳細関係にも関係していることです(.net 3.0より前)

2)設計を再考します。一般に、IDは不変であり、ギャップなどを考慮に入れる必要があります。これにより、すべてのデータベース操作の信頼性が大幅に向上し、はるかに簡単になります。別の列を使用して、ユーザーに提示する連番を維持できます。

于 2009-06-22T15:59:36.160 に答える