¿Es esto un error en la API de DataTable? Los cambios se almacenan / ejecutan en la "secuencia incorrecta"
-
06-07-2019 - |
Pregunta
Editar: cualquier comentario si crees que es un error de .NET o no sería apreciado.
Tengo un error que he logrado simplificar en el siguiente escenario:
Tengo una DataTable donde las claves principales deben mantenerse consecutivas, p. si inserta una fila entre otras filas, primero debe incrementar el ID de las filas sucesivas para hacer espacio, y luego insertar la fila.
Y si elimina una fila, debe disminuir la ID de las filas sucesivas para llenar el espacio dejado por la fila en la tabla.
Caso de prueba que funciona correctamente
Comience con 3 filas en la tabla, con los ID 1, 2 y 3.
Luego elimine ID = 2 y establezca ID = 2 donde ID = 3 (para llenar el vacío); Esto funciona correctamente. DataTable.GetChanges () contiene la fila eliminada y luego la fila modificada; cuando ejecuta dataAdapter.Update (tabla) se ejecuta bien.
Caso de prueba que no funciona
Sin embargo, si comienza con 2 filas (ID 1 y 2), configure ID = 3 donde ID = 2, e inserte ID = 2, luego confirme (o acepte) los cambios. Esto debería ser ahora el mismo estado que la primera prueba.
Luego realiza los mismos pasos que antes, es decir, elimina ID = 2 y establece ID = 2 donde ID = 3, pero ahora dataTable.GetChanges () está en el orden incorrecto. La primera fila es una fila modificada, y la segunda fila es la fila eliminada. Luego, si prueba dataAdapter.Update (tabla), dará una violación de clave principal: intentó modificar una fila a una fila ya existente antes de que se elimine.
Workorkund
Puedo pensar en una solución al problema, es decir, forzarlo para que las filas eliminadas se confirmen primero, y luego las filas modificadas y luego las filas agregadas. Pero ¿por qué sucede esto? ¿Hay otra solución?
Creo que he visto un "problema" similar antes con los diccionarios, que si agrega algunos elementos, elimínelos, vuelva a insertarlos, entonces no estarán en la misma secuencia que los agregó (cuando enumera el diccionario).
Aquí hay dos pruebas de NUnit que muestran el problema:
[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");
}
Salida:
Deleted: 2
Modified: 3 = 2
1 passed, 0 failed, 0 skipped, took 4.27 seconds (NUnit 2.4).
Siguiente prueba:
[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");
}
Salida:
Modified: 3 = 2
Deleted: 2
TestCase 'GetChanges_NotWorking'
failed:
1st row in GetChanges should be deleted row
Expected: Deleted
But was: Modified
Solución
No es un error, el punto es que está utilizando ID de una manera (muy) no estándar. Dos respuestas:
1) Use DataTable.GetChanges (DataRowState.Modified) para procesar sus actualizaciones en orden (creo que sería eliminado, modificado, insertado). Esto es lo que debe hacer también con las relaciones Maestro / Detalle (antes de .net 3.0)
2) Reconsidere su diseño, en general, los ID deben ser inmutables y permitir huecos, etc. Esto hará que todas las operaciones de su base de datos sean mucho más confiables y fáciles. Puede usar otra columna para mantener una numeración secuencial para presentar al usuario.