Editer: tout commentaire si vous pensez que c'est un bug .NET ou non serait apprécié.Est-ce un bogue dans l'API DataTable? Les changements sont stockés/exécutés dans la "mauvaise séquence"
J'ai un bug que j'ai réussi à simplifier au scénario suivant:
J'ai un DataTable où les clés primaires doivent être conservés consécutifs, par exemple Si vous insérez une ligne entre les autres lignes, vous devez d'abord incrémenter l'ID des lignes suivantes pour libérer de l'espace, puis insérer la ligne.
Et si vous supprimez une ligne, vous devez décrémenter l'ID de toutes les lignes suivantes pour remplir l'espace laissé par la ligne dans la table.
cas de test qui fonctionne correctement
Commencez avec 3 lignes de la table, avec les ID 1, 2 et 3.
Ensuite, supprimez ID = 2 et définissez ID = 2 où ID = 3 (pour combler l'écart); Cela fonctionne correctement. DataTable.GetChanges() contient la ligne supprimée, puis la ligne modifiée; Lorsque vous exécutez dataAdapter.Update (table), il s'exécute correctement.
cas de test qui ne fonctionne pas
Cependant, si vous commencez avec 2 lignes (ID 1 et 2), puis définissez ID = 3 où ID = 2, et insérer ID = 2, puis commit (ou accepter) changements. Cela devrait être maintenant le même état que le premier test.
Ensuite, vous faites les mêmes étapes qu'auparavant, c'est-à-dire supprimer ID = 2 et définir ID = 2 où ID = 3, mais maintenant dataTable.GetChanges() sont dans le mauvais ordre. La première ligne est une ligne modifiée et la deuxième ligne est la ligne supprimée. Ensuite, si vous essayez dataAdapter.Update (table), il donnera une violation de clé primaire - il a essayé de modifier une ligne à une ligne déjà existante avant de la supprimer.
Solution
Je peux penser à une solution au problème, à savoir la force pour que les lignes supprimées sont engagées d'abord, puis les lignes modifiées, puis ajouté des lignes. Mais pourquoi cela se passe-t-il? Y a-t-il une autre solution?
Je pense avoir déjà rencontré un "problème" similaire avec les dictionnaires: si vous ajoutez des éléments, supprimez-les, puis réinsérez-les, ils ne seront pas dans la même séquence que vous les avez ajoutés (lorsque vous énumérez le dictionnaire).
Voici deux tests NUnit qui montrent le problème:
[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");
}
Sortie:
Deleted: 2
Modified: 3 = 2
1 passed, 0 failed, 0 skipped, took 4.27 seconds (NUnit 2.4).
Prochain test:
[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");
}
Sortie:
Modified: 3 = 2
Deleted: 2
TestCase 'GetChanges_NotWorking'
failed:
1st row in GetChanges should be deleted row
Expected: Deleted
But was: Modified
+1 pour «en général, les ID doivent être immuables» et «Vous pouvez utiliser une autre colonne pour conserver une numérotation séquentielle à présenter à l'utilisateur». – TcKs
Je vois votre point de vue que les clés primaires ne devraient pas être utilisées de cette façon; mais il semble toujours que le DataTable me semble étrange, c'est-à-dire qu'il devrait fonctionner si je l'utilise de cette façon. Relié, pourquoi le comportement par défaut de DataAdapter.Update n'est pas traité dans l'ordre supprimé, modifié puis inséré. (BTW c'est l'ordre que j'avais l'intention de taper dans ma solution de contournement - je l'ai corrigé dans le texte original maintenant). – RickL
@Rick: L'adaptateur a une fonctionnalité 'limitée' (c'est-à-dire qu'il ne peut pas gérer les relations), donc si vous voulez quelque chose de non standard, vous devrez le faire vous-même. –