2010-04-26 6 views
7

Ho un genitore WinForm che ha un MyDataTable _dt come membro. Il tipo MyDataTable è stato creato nello strumento di progettazione "set di dati digitati" in Visual Studio 2005 (MyDataTable eredita da DataTable) _dt viene popolato da un db tramite ADO.NET. Sulla base di cambiamenti da interazione con l'utente in forma, elimina una riga dalla tabella in questo modo:Accesso alle righe eliminate da un DataTable

_dt.FindBySomeKey(_someKey).Delete();

Più tardi, _dt viene passato per valore a una forma di dialogo. Da lì, ho bisogno di eseguire la scansione attraverso tutte le righe per costruire una stringa:

  foreach (myDataTableRow row in _dt) 
      { 
       sbFilter.Append("'" + row.info + "',"); 
      } 

Il problema è che al momento di fare questo dopo una cancellazione, la seguente eccezione viene generata: DeletedRowInaccessibleException: Deleted row information cannot be accessed through the row.

Il lavoro intorno a quello attualmente sto usando (che si sente come un hack) è la seguente:

  foreach (myDataTableRow row in _dt) 
      { 
       if (row.RowState != DataRowState.Deleted && 
        row.RowState != DataRowState.Detached) 
       { 
        sbFilter.Append("'" + row.info + "',"); 
       } 
      } 

la mia domanda: E 'questo il modo corretto di fare questo? Perché le righe di accesso al ciclo foreach sono state taggate tramite il metodo Delete() ??

+0

Non è necessario controllare per 'Distaccato'. – SLaks

risposta

2

Si dovrà verificare:

_dt.DefaultView.RowStateFilter 

Penso che l'impostazione predefinita non mostrerà righe eliminate, forse si cambia da qualche parte.

È sempre possibile creare un RowView in più e controllarne il filtro ed eseguire il loop sulla vista.

+0

Ciò non influisce sull'enumeratore della tabella. – SLaks

+0

@Slaks: Ho scritto: 'e loop over the View' –

+0

Che perde le righe digitate. – SLaks

14

Il numero Delete method contrassegna una riga per la cancellazione; la riga non viene effettivamente rimossa finché non si chiama AcceptChanges.

Invece, chiamare _dt.Rows.Remove(_dt.FindBySomeKey(_someKey)), che accetterà anche la modifica.
Che ci crediate o no, Rows.Remove will completely remove the row, whereas row.Delete() won't.
Nota che se chiami Rows.Remove, la riga verrà definitivamente persa e non verrà eliminata dal database da un DataAdapter.

In C# 3, è possibile sostituire la sua dichiarazione if con il seguente metodo di estensione:

///<summary>Gets the rows in a typed DataTable that have not been deleted.</summary> 
public static EnumerableRowCollection<TRow> CurrentRows<TRow>(this TypedTableBase<TRow> table) where TRow : DataRow { 
    return table.Where(r => r.RowState != DataRowState.Deleted); 
} 

foreach (myDataTableRow row in _dt.CurrentRows()) 
{ 
    ... 

EDIT

Ecco un C# 2 versione:

static class Utils { 
    public static IEnumerable<TRow> CurrentRows(IEnumerable<TRow> table) where TRow : DataRow { 
     foreach(TRow row in table) { 
      if (row.RowState != DataRowState.Deleted) 
       yield return row; 
     } 
    } 
} 


foreach (myDataTableRow row in Utils.CurrentRows(_dt)) 
{ 

Si potrebbe inserire anche questa funzione nella classe parziale per la tabella digitata:

partial class MyTable { 
    public IEnumerable<MyRow> CurrentRows() { return Utils.CurrentRows(this); } 
} 
+0

Grazie per la risposta. In questo modo, ci saranno implicazioni quando cercherò di aggiornare il mio tavolo al db? _dt.Update() L'aggiornamento() sa di cancellare questa riga usando DeleteCommand o è "andato per sempre?" – Ken

+0

@Ken: No, le righe rimosse _non_ verranno eliminate durante un aggiornamento. Rimuovi! = Elimina –

+0

@Ken: buon punto; Assumerei che chiamare rimuovere causi 'Aggiorna' per non cancellarlo. Provalo. – SLaks

0

Non sono sicuro di ciò, ma penso che se si chiama _dt.AcceptChanges() dopo aver eliminato una o più righe, non si "vedranno" le righe eliminate quando si scorre l'insieme di righe della tabella di dati.

+0

AcceptChanges rimuove lo stato della riga e ho bisogno di questo stato più avanti quando eseguo un _dt.Update() per mantenere le modifiche sul DB. Probabilmente chiamerei AcceptChanges() dopo l'aggiornamento. Grazie! – Ken

1

Utilizzare GetChanges():

foreach (myDataTableRow row in _dt.GetChanges(DataRowState.Added)) 
{ 
    sbFilter.Append("'" + row.info + "',"); 
} 

Se si desidera ottenere tutte aggiunto e modificato, utilizzare OR bit per bit su aggiunto e modifica:

foreach (myDataTableRow row in 
    _dt.GetChanges(DataRowState.Added | DataRowState.Modified)) 
{ 
    sbFilter.Append("'" + row.info + "',"); 
} 
+1

L'idea giusta, ma in realtà produce un errore del compilatore perché DataTable non contiene un metodo pubblico GetEnumerator(). – Ken

+0

Ciò non restituirà le righe invariate. – SLaks

+0

@SLaks: nonostante il nome GetChanges, puoi ottenere righe invariate, _dt.GetChanges (DataRowSate.Unchanged) –

2

Quello che stai facendo saltare le righe nella tua iterazione è corretta, controllo regolarmente il RowState quando eseguo il loop su un DataTable che può essere modificato.

In alcuni casi, credo che si desideri il valore della riga originale prima che la riga fosse contrassegnata come eliminata. C'è un'opzione di indice secondario quando si recupera un particolare valore di datarow.

string st = dr["columnname", DataRowVersion.Original].ToString(); // c# 

dim st As String = dr("columnname", DataRowVersion.Original).ToString() ' vb.net 

Mi sono bloccato su questo più volte in passato.

Per quanto riguarda il metodo GetChanges (RowState), non dimenticare di controllare un ritorno nullo, se non ci sono righe di quel RowState, il DataTable restituito è nullo (penso che dovrebbe restituire una tabella con zero righe)