È un bug nell'API DataTable? Le modifiche vengono archiviate / eseguite nella sequenza & # 8220; errata & # 8221;

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

  •  06-07-2019
  •  | 
  •  

Domanda

Modifica: sarebbe gradito qualsiasi commento, sia che si pensi che si tratti o meno di un bug .NET.

Ho un bug che sono riuscito a semplificare al seguente scenario:

Ho una DataTable in cui le chiavi primarie devono essere mantenute consecutive, ad es. se inserisci una riga tra altre righe, devi prima incrementare l'ID delle righe successive per fare spazio, quindi inserire la riga.

E se si elimina una riga, è necessario ridurre l'ID di tutte le righe successive per riempire lo spazio lasciato dalla riga nella tabella.

Test case che funziona correttamente

Inizia con 3 righe nella tabella, con ID 1, 2 e 3.

Quindi elimina ID = 2 e imposta ID = 2 dove ID = 3 (per riempire il vuoto); questo funziona correttamente. DataTable.GetChanges () contiene la riga eliminata e quindi la riga modificata; quando si esegue dataAdapter.Update (tabella) viene eseguito correttamente.

Test case che non funziona

Tuttavia, se inizi con 2 righe (ID 1 e 2), imposta ID = 3 dove ID = 2 e inserisci ID = 2, quindi esegui il commit (o accetta) le modifiche. Questo dovrebbe essere ora lo stesso stato del primo test.

Quindi fai gli stessi passaggi di prima, ovvero elimina ID = 2 e imposta ID = 2 dove ID = 3, ma ora i datiTable.GetChanges () sono nell'ordine sbagliato. La prima riga è una riga modificata e la seconda riga è la riga eliminata. Quindi, se provi dataAdapter.Update (tabella), si verificherà una violazione della chiave primaria: tenterà di modificare una riga in una riga già esistente prima di eliminarla.

Soluzione

Posso pensare a una soluzione alternativa al problema, ovvero forzarlo in modo che le righe eliminate vengano prima impegnate, quindi le righe modificate e quindi le righe aggiunte. Ma perché sta succedendo questo? C'è un'altra soluzione?

Penso di aver visto un simile "problema". prima con i dizionari, che se aggiungi alcuni elementi, li elimini, li reinserisci, quindi non saranno nella stessa sequenza che li hai aggiunti (quando si enumera il dizionario).

Ecco due test NUnit che mostrano il 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");
}

Output:

Deleted: 2
Modified: 3 = 2

1 passed, 0 failed, 0 skipped, took 4.27 seconds (NUnit 2.4).

Test successivo:

[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");
}

Output:

Modified: 3 = 2
Deleted: 2
TestCase 'GetChanges_NotWorking'
failed: 
  1st row in GetChanges should be deleted row
  Expected: Deleted
  But was:  Modified
È stato utile?

Soluzione

Non è un bug, il punto è che stai usando gli ID in un modo (molto) non standard. Due risposte:

1) Utilizzare DataTable.GetChanges (DataRowState.Modified) per elaborare gli aggiornamenti in ordine (penso che verrebbero eliminati, modificati, inseriti). Questo è ciò che hai a che fare anche con le relazioni Master / Detail (prima di .net 3.0)

2) Ripensare la progettazione, in generale gli ID dovrebbero essere immutabili e consentire spazi vuoti ecc. Ciò renderà tutte le operazioni del database molto più affidabili e molto più facili. Puoi utilizzare un'altra colonna per mantenere una numerazione sequenziale da presentare all'utente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top