¿Es esto un error en la API de DataTable? Los cambios se almacenan / ejecutan en la "secuencia incorrecta"

StackOverflow https://stackoverflow.com/questions/1027095

  •  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
¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top