Salvare nuovamente i dati nel database nelle applicazioni .NET Framework

Nota

I set di dati e le classi correlate sono tecnologie .NET Framework legacy dei primi anni '2000 che consentono alle applicazioni di lavorare con i dati in memoria mentre le applicazioni vengono disconnesse dal database. Sono particolarmente utili per le applicazioni che consentono agli utenti di modificare i dati e rendere persistenti le modifiche apportate al database. Anche se i set di dati hanno dimostrato di essere una tecnologia molto efficace, è consigliabile che le nuove applicazioni .NET usino Entity Framework Core. Entity Framework offre un modo più naturale per usare i dati tabulari come modelli a oggetti e ha un'interfaccia di programmazione più semplice.

Il set di dati è una copia in memoria dei dati. Se si modificano tali dati, è consigliabile salvare tali modifiche nel database. Questa operazione viene eseguita in uno dei tre modi seguenti:

  • Chiamando uno dei Update metodi di un TableAdapter

  • Chiamando uno dei DBDirect metodi di TableAdapter

  • Chiamando il UpdateAll metodo in TableAdapterManager generato da Visual Studio quando il set di dati contiene tabelle correlate ad altre tabelle nel set di dati

Quando si associano tabelle del set di dati ai controlli in una pagina Windows Form o XAML, l'architettura del data binding funziona automaticamente.

Se si ha familiarità con TableAdapters, è possibile passare direttamente a uno di questi argomenti:

Argomento Descrizione
Inserire nuovi record in un database Come eseguire aggiornamenti e inserimenti usando oggetti TableAdapter o Command
Aggiornare i dati mediante un TableAdapter Come eseguire gli aggiornamenti con TableAdapters
Aggiornamento gerarchico Come eseguire gli aggiornamenti da un set di dati con due o più tabelle correlate
Gestire un'eccezione di concorrenza Come gestire le eccezioni quando due utenti tentano di modificare gli stessi dati in un database contemporaneamente
Procedura: Salvare dati usando una transazione Come salvare i dati in una transazione usando il sistema. Spazio dei nomi Transactions e un oggetto TransactionScope
Salvare i dati in una transazione Procedura dettagliata che crea un'applicazione Windows Form per illustrare il salvataggio dei dati in un database all'interno di una transazione
Salvare dati in un database (a più tabelle) Come modificare i record e salvare le modifiche in più tabelle nel database
Salvare dati da un oggetto in un database Come passare dati da un oggetto che non si trova in un set di dati a un database usando un metodo DbDirect TableAdapter
PSalvare dati con i metodi DBDirect di TableAdapter Come usare TableAdapter per inviare query SQL direttamente al database
Salvare un set di dati come XML Come salvare un set di dati in un documento XML

Aggiornamenti in due fasi

L'aggiornamento di un'origine dati è un processo in due passaggi. Il primo passaggio consiste nell'aggiornare il set di dati con nuovi record, record modificati o record eliminati. Se l'applicazione non invia mai tali modifiche all'origine dati, l'aggiornamento verrà completato.

Se si inviano le modifiche al database, è necessario un secondo passaggio. Se non si usano controlli associati a dati, è necessario chiamare manualmente il Update metodo dello stesso TableAdapter (o adattatore dati) usato per popolare il set di dati. Tuttavia, è anche possibile usare adattatori diversi, ad esempio per spostare i dati da un'origine dati a un'altra o per aggiornare più origini dati. Se non si usa il data binding e si salvano le modifiche per le tabelle correlate, è necessario creare manualmente un'istanza di una variabile della classe generata automaticamente TableAdapterManager e quindi chiamare il relativo UpdateAll metodo.

Conceptual diagram of dataset updates

Un set di dati contiene raccolte di tabelle che contengono raccolte di righe. Se si intende aggiornare un'origine dati sottostante in un secondo momento, è necessario utilizzare i metodi nella proprietà durante l'aggiunta o la DataTable.DataRowCollection rimozione di righe. Questi metodi eseguono il rilevamento delle modifiche necessario per aggiornare l'origine dati. Se si chiama la RemoveAt raccolta nella proprietà Rows, l'eliminazione non verrà comunicata al database.

Unire set di dati

È possibile aggiornare il contenuto di un set di dati unendolo a un altro set di dati. Ciò comporta la copia del contenuto di un set di dati di origine nel set di dati chiamante (detto set di dati di destinazione ). Quando si uniscono set di dati, i nuovi record nel set di dati di origine vengono aggiunti al set di dati di destinazione. Inoltre, al set di dati di destinazione vengono aggiunte colonne aggiuntive nel set di dati di origine. L'unione di set di dati è utile quando si dispone di un set di dati locale e si ottiene un secondo set di dati da un'altra applicazione. È utile anche quando si ottiene un secondo set di dati da un componente, ad esempio un servizio Web XML, o quando è necessario integrare i dati da più set di dati.

Quando si uniscono set di dati, è possibile passare un argomento booleano (preserveChanges) che indica al Merge metodo se conservare le modifiche esistenti nel set di dati di destinazione. Poiché i set di dati mantengono più versioni di record, è importante tenere presente che più versioni dei record vengono unite. La tabella seguente illustra come viene unito un record in due set di dati:

DataRowVersion DataSet di destinazione Set di dati di origine
Originale James Wilson James C. Wilson
Corrente Jim Wilson James C. Wilson

Chiamando il Merge metodo nella tabella precedente con preserveChanges=false targetDataset.Merge(sourceDataset) i risultati nei dati seguenti:

DataRowVersion DataSet di destinazione Set di dati di origine
Originale James C. Wilson James C. Wilson
Corrente James C. Wilson James C. Wilson

Chiamata del Merge metodo con preserveChanges = true targetDataset.Merge(sourceDataset, true) i risultati nei dati seguenti:

DataRowVersion DataSet di destinazione Set di dati di origine
Originale James C. Wilson James C. Wilson
Corrente Jim Wilson James C. Wilson

Attenzione

preserveChanges = true Nello scenario, se il RejectChanges metodo viene chiamato su un record nel set di dati di destinazione, ripristina i dati originali dal set di dati di origine. Ciò significa che se si tenta di aggiornare l'origine dati originale con il set di dati di destinazione, potrebbe non essere possibile trovare la riga originale da aggiornare. È possibile impedire una violazione della concorrenza compilando un altro set di dati con i record aggiornati dall'origine dati e quindi eseguendo un'unione per impedire una violazione della concorrenza. Una violazione di concorrenza si verifica quando un altro utente modifica un record nell'origine dati dopo il riempimento del set di dati.

Aggiornare i vincoli

Per apportare modifiche a una riga di dati esistente, aggiungere o aggiornare i dati nelle singole colonne. Se il set di dati contiene vincoli, ad esempio chiavi esterne o vincoli non nullable, è possibile che il record possa temporaneamente trovarsi in uno stato di errore durante l'aggiornamento. Ovvero, può trovarsi in uno stato di errore dopo aver completato l'aggiornamento di una colonna, ma prima di passare a quello successivo.

Per evitare violazioni di vincoli prematuri, è possibile sospendere temporaneamente i vincoli di aggiornamento. Questo serve due scopi:

  • Impedisce che venga generato un errore dopo aver completato l'aggiornamento di una colonna, ma non è stato avviato l'aggiornamento di un'altra.

  • Impedisce la generazione di determinati eventi di aggiornamento (eventi spesso usati per la convalida).

Nota

In Windows Form, l'architettura di data binding incorporata in DataGrid sospende il controllo dei vincoli fino a quando lo stato attivo non si sposta all'esterno di una riga e non è necessario chiamare in modo esplicito i BeginEditmetodi , EndEdito CancelEdit .

I vincoli vengono disabilitati automaticamente quando il Merge metodo viene richiamato in un set di dati. Al termine dell'unione, se sono presenti vincoli sul set di dati che non è possibile abilitare, viene generata un'eccezione ConstraintException . In questo caso, la EnforceConstraints proprietà è impostata su false, e tutte le violazioni dei vincoli devono essere risolte prima di reimpostare la EnforceConstraints proprietà su true.

Dopo aver completato un aggiornamento, è possibile riabilitare il controllo dei vincoli, che abilita nuovamente gli eventi di aggiornamento e li genera.

Per altre informazioni sulla sospensione degli eventi, vedere Disattivare i vincoli durante il riempimento di un set di dati.

Errori di aggiornamento del set di dati

Quando si aggiorna un record in un set di dati, è possibile che si verifichi un errore. Ad esempio, è possibile scrivere inavvertitamente dati del tipo errato in una colonna o dati troppo lunghi o dati con altri problemi di integrità. In alternativa, potrebbero essere presenti controlli di convalida specifici dell'applicazione che possono generare errori personalizzati durante qualsiasi fase di un evento di aggiornamento. Per altre informazioni, vedere Convalidare i dati nei set di dati.

Mantenere le informazioni sulle modifiche

Le informazioni sulle modifiche in un set di dati vengono mantenute in due modi: contrassegnando le righe che indicano che sono state modificate (RowState) e mantenendo più copie di un record (DataRowVersion). Usando queste informazioni, i processi possono determinare le modifiche apportate al set di dati e inviare aggiornamenti appropriati all'origine dati.

Proprietà RowState

La RowState proprietà di un DataRow oggetto è un valore che fornisce informazioni sullo stato di una determinata riga di dati.

La tabella seguente illustra in dettaglio i valori possibili dell'enumerazione DataRowState :

Valore DataRowState Descrizione
Added La riga è stata aggiunta come elemento a un oggetto DataRowCollection. Una riga in questo stato non ha una versione originale corrispondente perché non esiste quando è stato chiamato l'ultimo AcceptChanges metodo.
Deleted La riga è stata eliminata utilizzando l'oggetto Delete di un DataRow oggetto .
Detached La riga è stata creata ma non fa parte di alcun DataRowCollectionoggetto . Un DataRow oggetto si trova in questo stato immediatamente dopo la creazione, prima che sia stato aggiunto a una raccolta e dopo che è stato rimosso da un insieme.
Modified Un valore di colonna nella riga è stato modificato in qualche modo.
Unchanged La riga non è stata modificata dall'ultima AcceptChanges chiamata.

DataRowVersion (enumerazione)

I set di dati mantengono più versioni dei record. I DataRowVersion campi vengono utilizzati quando si recupera il valore trovato in un DataRow oggetto utilizzando la Item[] proprietà o il GetChildRows metodo dell'oggetto DataRow .

La tabella seguente illustra in dettaglio i valori possibili dell'enumerazione DataRowVersion :

Valore di DataRowVersion Descrizione
Current La versione corrente di un record contiene tutte le modifiche eseguite sul record dall'ultima chiamata AcceptChanges . Se la riga è stata eliminata, non è disponibile alcuna versione corrente.
Default Valore predefinito di un record, come definito dallo schema del set di dati o dall'origine dati.
Original La versione originale di un record è una copia del record perché è stata eseguita l'ultima volta che è stato eseguito il commit delle modifiche nel set di dati. In termini pratici, si tratta in genere della versione di un record come letto da un'origine dati.
Proposed Versione proposta di un record disponibile temporaneamente mentre ci si trova al centro di un aggiornamento, ovvero tra il tempo chiamato il BeginEdit metodo e il EndEdit metodo . In genere si accede alla versione proposta di un record in un gestore per un evento, RowChangingad esempio . La chiamata al CancelEdit metodo inverte le modifiche ed elimina la versione proposta della riga di dati.

Le versioni originali e correnti sono utili quando le informazioni di aggiornamento vengono trasmesse a un'origine dati. In genere, quando un aggiornamento viene inviato all'origine dati, le nuove informazioni per il database si trovano nella versione corrente di un record. Le informazioni della versione originale vengono usate per individuare il record da aggiornare.

Ad esempio, in un caso in cui la chiave primaria di un record viene modificata, è necessario un modo per individuare il record corretto nell'origine dati per aggiornare le modifiche. Se non esiste alcuna versione originale, è probabile che il record venga aggiunto all'origine dati, con conseguente non solo in un record aggiuntivo indesiderato, ma in un record non accurato e non aggiornato. Le due versioni vengono usate anche nel controllo della concorrenza. È possibile confrontare la versione originale con un record nell'origine dati per determinare se il record è stato modificato dopo il caricamento nel set di dati.

La versione proposta è utile quando è necessario eseguire la convalida prima di eseguire effettivamente il commit delle modifiche nel set di dati.

Anche se i record sono stati modificati, non sono sempre presenti versioni originali o correnti di tale riga. Quando si inserisce una nuova riga nella tabella, non esiste una versione originale, ma solo una versione corrente. Analogamente, se si elimina una riga chiamando il metodo della Delete tabella, è presente una versione originale, ma non una versione corrente.

È possibile verificare se esiste una versione specifica di un record eseguendo una query sul metodo di una riga di HasVersion dati. È possibile accedere a una delle due versioni di un record passando un DataRowVersion valore di enumerazione come argomento facoltativo quando si richiede il valore di una colonna.

Ottenere record modificati

È prassi comune non aggiornare ogni record in un set di dati. Ad esempio, un utente potrebbe lavorare con un controllo Windows Form DataGridView che visualizza molti record. Tuttavia, l'utente potrebbe aggiornare solo alcuni record, eliminarli uno e inserirlo uno nuovo. I set di dati e le tabelle dati forniscono un metodo (GetChanges) per restituire solo le righe modificate.

È possibile creare subset di record modificati usando il GetChanges metodo della tabella dati (GetChanges) o del set di dati stesso (GetChanges). Se si chiama il metodo per la tabella dati, restituisce una copia della tabella con solo i record modificati. Analogamente, se si chiama il metodo nel set di dati, si ottiene un nuovo set di dati con solo record modificati.

GetChanges da solo restituisce tutti i record modificati. Al contrario, passando l'oggetto desiderato DataRowState come parametro al GetChanges metodo , è possibile specificare il subset di record modificati desiderati: record appena aggiunti, record contrassegnati per l'eliminazione, i record scollegati o i record modificati.

Ottenere un subset di record modificati è utile quando si desidera inviare record a un altro componente per l'elaborazione. Anziché inviare l'intero set di dati, è possibile ridurre il sovraccarico di comunicazione con l'altro componente ottenendo solo i record necessari per il componente.

Eseguire il commit delle modifiche nel set di dati

Quando vengono apportate modifiche nel set di dati, viene impostata la RowState proprietà delle righe modificate. Le versioni originali e correnti dei record vengono stabilite, mantenute e rese disponibili dalla RowVersion proprietà . I metadati archiviati nelle proprietà di queste righe modificate sono necessari per l'invio degli aggiornamenti corretti all'origine dati.

Se le modifiche riflettono lo stato corrente dell'origine dati, non è più necessario mantenere queste informazioni. In genere, ci sono due volte in cui il set di dati e la relativa origine sono sincronizzati:

  • Subito dopo aver caricato le informazioni nel set di dati, ad esempio quando si leggono i dati dall'origine.

  • Dopo l'invio delle modifiche dal set di dati all'origine dati , ma non prima, perché si perderebbero le informazioni sulle modifiche necessarie per inviare modifiche al database.

È possibile eseguire il commit delle modifiche in sospeso nel set di dati chiamando il AcceptChanges metodo . In genere, AcceptChanges viene chiamato nei seguenti orari:

  • Dopo aver caricato il set di dati. Se si carica un set di dati chiamando il metodo di Fill tableAdapter, l'adattatore esegue automaticamente il commit delle modifiche. Tuttavia, se si carica un set di dati unendo un altro set di dati, è necessario eseguire manualmente il commit delle modifiche.

    Nota

    È possibile impedire all'adapter di eseguire automaticamente il commit delle modifiche quando si chiama il Fill metodo impostando la AcceptChangesDuringFill proprietà dell'adapter su false. Se è impostato su false, l'oggetto RowState di ogni riga inserita durante il riempimento viene impostata su Added.

  • Dopo aver inviato le modifiche del set di dati a un altro processo, ad esempio un servizio Web XML.

    Attenzione

    Il commit della modifica in questo modo cancella tutte le informazioni sulle modifiche. Non eseguire il commit delle modifiche fino al termine dell'esecuzione di operazioni che richiedono all'applicazione di conoscere le modifiche apportate nel set di dati.

Questo metodo esegue le operazioni seguenti:

  • Scrive la Current versione di un record nella relativa Original versione e sovrascrive la versione originale.

  • Rimuove qualsiasi riga in cui la RowState proprietà è impostata su Deleted.

  • Imposta la RowState proprietà di un record su Unchanged.

Il AcceptChanges metodo è disponibile a tre livelli. È possibile chiamarlo su un DataRow oggetto per eseguire il commit delle modifiche solo per tale riga. È anche possibile chiamarlo su un DataTable oggetto per eseguire il commit di tutte le righe di una tabella. Infine, è possibile chiamarlo sull'oggetto DataSet per eseguire il commit di tutte le modifiche in sospeso in tutti i record di tutte le tabelle del set di dati.

Nella tabella seguente vengono descritte le modifiche di cui viene eseguito il commit in base all'oggetto su cui viene chiamato il metodo :

metodo Risultato
System.Data.DataRow.AcceptChanges Viene eseguito il commit delle modifiche solo nella riga specifica.
System.Data.DataTable.AcceptChanges Viene eseguito il commit delle modifiche in tutte le righe della tabella specifica.
System.Data.DataSet.AcceptChanges Viene eseguito il commit delle modifiche in tutte le righe di tutte le tabelle del set di dati.

Nota

Se si carica un set di dati chiamando il metodo di Fill TableAdapter, non è necessario accettare in modo esplicito le modifiche. Per impostazione predefinita, il Fill metodo chiama il AcceptChanges metodo al termine del popolamento della tabella dati.

Un metodo correlato, RejectChanges, annulla l'effetto delle modifiche copiando nuovamente la Original versione nella Current versione dei record. Inoltre, imposta nuovamente l'oggetto RowState di ogni record su Unchanged.

Convalida dei dati

Per verificare che i dati nell'applicazione soddisfino i requisiti dei processi a cui vengono passati, spesso è necessario aggiungere la convalida. Ciò potrebbe comportare il controllo della voce di un utente in un modulo corretto, la convalida dei dati inviati all'applicazione da un'altra applicazione o anche la verifica di tali informazioni calcolate all'interno del componente rientrano nei vincoli dei requisiti dell'origine dati e dell'applicazione.

È possibile convalidare i dati in diversi modi:

  • Nel livello aziendale aggiungere codice all'applicazione per convalidare i dati. Il set di dati è un'unica posizione in cui è possibile eseguire questa operazione. Il set di dati offre alcuni dei vantaggi della convalida back-end, ad esempio la possibilità di convalidare le modifiche durante la modifica dei valori di colonna e di riga. Per altre informazioni, vedere Convalidare i dati nei set di dati.

  • Nel livello presentazione aggiungere la convalida ai moduli. Per altre informazioni, vedere Convalida dell'input utente in Windows Form.

  • Nel back-end dei dati, inviando dati all'origine dati, ad esempio il database, e consentendogli di accettare o rifiutare i dati. Se si lavora con un database con funzionalità sofisticate per la convalida dei dati e la fornitura di informazioni sugli errori, questo può essere un approccio pratico perché è possibile convalidare i dati indipendentemente da dove provengono. Tuttavia, questo approccio potrebbe non soddisfare i requisiti di convalida specifici dell'applicazione. Inoltre, la convalida dei dati dell'origine dati può comportare numerosi round trip all'origine dati, a seconda del modo in cui l'applicazione facilita la risoluzione degli errori di convalida generati dal back-end.

    Importante

    Quando si usano comandi dati con una CommandType proprietà impostata su Text, controllare attentamente le informazioni inviate da un client prima di passarla al database. Qualche utente malintenzionato potrebbe tentare di inviare (inserire) istruzioni SQL modificate o aggiuntive, allo scopo di ottenere un accesso non autorizzato o di danneggiare il database. Prima di trasferire l'input dell'utente in un database, verificare sempre che le informazioni siano valide. È consigliabile usare sempre query o stored procedure con parametri, quando possibile.

Trasmettere gli aggiornamenti all'origine dati

Dopo aver apportato modifiche in un set di dati, è possibile trasmettere le modifiche a un'origine dati. In genere, è possibile eseguire questa operazione chiamando il Update metodo di un TableAdapter (o un adattatore dati). Il metodo esegue ciclicamente ogni record in una tabella di dati, determina il tipo di aggiornamento necessario (aggiornamento, inserimento o eliminazione), se presente e quindi esegue il comando appropriato.

Come illustrazione di come vengono eseguiti gli aggiornamenti, si supponga che l'applicazione usi un set di dati che contiene una singola tabella dati. L'applicazione recupera due righe dal database. Dopo il recupero, la tabella dei dati in memoria ha un aspetto simile al seguente:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Unchanged)    c400         Nancy Buchanan    Pending

L'applicazione modifica lo stato di Nancy Buchanan in "Preferred". In seguito a questa modifica, il valore della proprietà per tale RowState riga cambia da Unchanged a Modified. Il valore della RowState proprietà per la prima riga rimane Unchanged. La tabella dati è ora simile alla seguente:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Modified)     c400         Nancy Buchanan    Preferred

Tramite l'applicazione in uso viene ora chiamato il metodo Update per trasmettere il dataset al database. Il metodo controlla a sua volta ogni riga. Per la prima riga, il metodo non trasmette alcuna istruzione SQL al database perché tale riga non è stata modificata da quando è stata recuperata originariamente dal database.

Per la seconda riga, tuttavia, il Update metodo richiama automaticamente il comando di dati corretto e lo trasmette al database. La sintassi specifica dell'istruzione SQL dipende dal dialetto di SQL supportato dall'archivio dati sottostante. Tuttavia, i tratti generali seguenti dell'istruzione SQL trasmessa sono degni di nota:

  • L'istruzione SQL trasmessa è un'istruzione UPDATE. L'adapter sa usare un'istruzione UPDATE perché il valore della RowState proprietà è Modified.

  • L'istruzione SQL trasmessa include una clausola WHERE che indica che la destinazione dell'istruzione UPDATE è la riga in cui CustomerID = 'c400'. Questa parte dell'istruzione edizione Standard LECT distingue la riga di destinazione da tutte le altre perché CustomerID è la chiave primaria della tabella di destinazione. Le informazioni per la clausola WHERE derivano dalla versione originale del record (DataRowVersion.Original), nel caso in cui i valori necessari per identificare la riga siano stati modificati.

  • L'istruzione SQL trasmessa include la clausola edizione Standard T per impostare i nuovi valori delle colonne modificate.

    Nota

    Se la proprietà di UpdateCommand TableAdapter è stata impostata sul nome di una stored procedure, l'adapter non crea un'istruzione SQL. Richiama invece la stored procedure con i parametri appropriati passati.

Passare parametri

In genere si usano parametri per passare i valori per i record che verranno aggiornati nel database. Quando il metodo tableAdapter Update esegue un'istruzione UPDATE, deve compilare i valori dei parametri. Ottiene questi valori dalla Parameters raccolta per il comando dati appropriato, in questo caso l'oggetto nell'oggetto UpdateCommand TableAdapter.

Se sono stati usati gli strumenti di Visual Studio per generare un adattatore dati, l'oggetto UpdateCommand contiene una raccolta di parametri che corrispondono a ogni segnaposto di parametro nell'istruzione .

La System.Data.SqlClient.SqlParameter.SourceColumn proprietà di ogni parametro punta a una colonna nella tabella dati. Ad esempio, la SourceColumn proprietà per i au_id parametri e Original_au_id viene impostata su qualsiasi colonna della tabella dati contenga l'ID autore. Quando viene eseguito il metodo dell'adapter Update , legge la colonna dell'ID autore dal record che viene aggiornato e inserisce i valori nell'istruzione .

In un'istruzione UPDATE è necessario specificare sia i nuovi valori (quelli che verranno scritti nel record) sia i valori precedenti (in modo che il record possa trovarsi nel database). Esistono quindi due parametri per ogni valore: uno per la clausola edizione Standard T e uno diverso per la clausola WHERE. Entrambi i parametri leggono i dati dal record che viene aggiornato, ma ottengono versioni diverse del valore della colonna in base alla proprietà del SourceVersion parametro. Il parametro per la clausola edizione Standard T ottiene la versione corrente e il parametro per la clausola WHERE ottiene la versione originale.

Nota

È anche possibile impostare i valori nella Parameters raccolta manualmente nel codice, che in genere si farebbe in un gestore eventi per l'evento dell'adattatore RowChanging dati.