Concorrenza ottimisticaOptimistic Concurrency

In un ambiente con più utenti sono disponibili due modelli per l'aggiornamento di dati in un database: la concorrenza ottimistica e la concorrenza pessimistica.In a multiuser environment, there are two models for updating data in a database: optimistic concurrency and pessimistic concurrency. L'oggetto DataSet è stato progettato per favorire l'uso della concorrenza ottimistica per attività di lunga durata, quali la gestione in remoto dei dati e l'interazione con i dati.The DataSet object is designed to encourage the use of optimistic concurrency for long-running activities, such as remoting data and interacting with data.

La concorrenza pessimistica implica il blocco di righe nell'origine dati per impedire agli altri utenti di apportare modifiche ai dati che possano influire sugli utenti correnti.Pessimistic concurrency involves locking rows at the data source to prevent other users from modifying data in a way that affects the current user. In un modello pessimistico, quando un utente esegue un'azione che provoca l'applicazione di un blocco, agli altri utenti non sarà consentito eseguire azioni che possano entrare in conflitto con tale blocco, fino a quando tale blocco non verrà rilasciato dal relativo proprietario.In a pessimistic model, when a user performs an action that causes a lock to be applied, other users cannot perform actions that would conflict with the lock until the lock owner releases it. Questo tipo di modello viene usato principalmente in ambienti in cui il conflitto per l'uso di dati è elevato e il dispendio di memoria dovuto alla protezione di dati tramite blocchi è inferiore al dispendio relativo all'annullamento di transazioni in caso di conflitti di concorrenza.This model is primarily used in environments where there is heavy contention for data, so that the cost of protecting data with locks is less than the cost of rolling back transactions if concurrency conflicts occur.

Pertanto, in un modello di concorrenza pessimistica, un blocco viene stabilito da un utente che aggiorna una riga.Therefore, in a pessimistic concurrency model, a user who updates a row establishes a lock. Fino a che tale utente non completa l'aggiornamento e non rilascia il blocco, nessun altro utente sarà in grado di modificare tale riga.Until the user has finished the update and released the lock, no one else can change that row. La concorrenza pessimistica è quindi consigliabile in caso di durate limitate dei blocchi, ad esempio nell'elaborazione a livello di programmazione dei record,For this reason, pessimistic concurrency is best implemented when lock times will be short, as in programmatic processing of records. ma non è un'opzione scalabile nel caso in cui gli utenti interagiscano con i dati e provochino il blocco dei record per periodi di tempo relativamente lunghi.Pessimistic concurrency is not a scalable option when users are interacting with data and causing records to be locked for relatively large periods of time.

Nota

Se è necessario aggiornare più righe nella stessa operazione, la creazione di una transazione garantisce una maggior scalabilità rispetto al blocco associato alla concorrenza pessimistica.If you need to update multiple rows in the same operation, then creating a transaction is a more scalable option than using pessimistic locking.

Al contrario, gli utenti che usano la concorrenza ottimistica sono in grado di leggere una riga senza bloccarla.By contrast, users who use optimistic concurrency do not lock a row when reading it. Quando un utente desidera aggiornare una riga, è necessario che l'applicazione stabilisca se tale riga è stata modificata da un altro utente successivamente alla lettura.When a user wants to update a row, the application must determine whether another user has changed the row since it was read. La concorrenza ottimistica viene solitamente usata in ambienti in cui il conflitto per l'uso dei dati non è elevato.Optimistic concurrency is generally used in environments with a low contention for data. Questo tipo di concorrenza consente un miglioramento delle prestazioni, poiché non è richiesto alcun blocco dei record, operazione che richiede risorse di server aggiuntive.Optimistic concurrency improves performance because no locking of records is required, and locking of records requires additional server resources. Per mantenere i blocchi dei record, inoltre, è necessaria una connessione permanente al server database.Also, in order to maintain record locks, a persistent connection to the database server is required. Poiché tali blocchi non sono necessari se si usa un modello di concorrenza ottimistica, le connessioni al server sono in grado di soddisfare un numero superiore di client in un intervallo di tempo minore.Because this is not the case in an optimistic concurrency model, connections to the server are free to serve a larger number of clients in less time.

In un modello di concorrenza ottimistica si verifica una violazione nel caso in cui, dopo che un utente riceve un valore dal database, tale valore viene modificato da un altro utente prima che il primo utente abbia effettuato un tentativo di modifica.In an optimistic concurrency model, a violation is considered to have occurred if, after a user receives a value from the database, another user modifies the value before the first user has attempted to modify it. L'esempio seguente consente di illustrare in modo migliore come viene risolta una violazione di concorrenza da parte del server.How the server resolves a concurrency violation is best shown by first describing the following example.

Nella tabella seguente viene mostrato un esempio di concorrenza ottimistica.The following tables follow an example of optimistic concurrency.

Alle ore 13.00 l'Utente1 legge una riga dal database contenente i seguenti valori:At 1:00 p.m., User1 reads a row from the database with the following values:

LastName FirstName CustIDCustID LastName FirstName

Bob Smith 101101 Smith Bob

Nome colonnaColumn name Valore originaleOriginal value Valore correnteCurrent value Valore nel databaseValue in database
CustIDCustID 101101 101101 101101
LastNameLastName DalziSmith DalziSmith DalziSmith
FirstNameFirstName MariaBob MariaBob MariaBob

Alle ore 13.01 l'Utente2 legge la stessa colonna.At 1:01 p.m., User2 reads the same row.

In 1 ore 13.03 l'utente2 modifica FirstName da "Maria" a "Maria Teresa" e aggiorna il database.At 1:03 p.m., User2 changes FirstName from "Bob" to "Robert" and updates the database.

Nome colonnaColumn name Valore originaleOriginal value Valore correnteCurrent value Valore nel databaseValue in database
CustIDCustID 101101 101101 101101
LastNameLastName DalziSmith DalziSmith DalziSmith
FirstNameFirstName MariaBob Maria TeresaRobert MariaBob

L'aggiornamento viene effettuato correttamente, poiché i valori presenti nel database al momento dell'aggiornamento corrispondono ai valori originali a disposizione dell'Utente2.The update succeeds because the values in the database at the time of update match the original values that User2 has.

Alle ore 13.05 l'Utente1 cambia il nome di "Maria" in "Teresa" e cerca di aggiornare la riga.At 1:05 p.m., User1 changes "Bob"'s first name to "James" and tries to update the row.

Nome colonnaColumn name Valore originaleOriginal value Valore correnteCurrent value Valore nel databaseValue in database
CustIDCustID 101101 101101 101101
LastNameLastName DalziSmith DalziSmith DalziSmith
FirstNameFirstName MariaBob TeresaJames Maria TeresaRobert

A questo punto l'Utente1 rileva una violazione della concorrenza ottimistica, poiché i valori del database ("Maria Teresa") non corrispondono più ai valori originali previsti dall'Utente1 ("Maria").At this point, User1 encounters an optimistic concurrency violation because the value in the database ("Robert") no longer matches the original value that User1 was expecting ("Bob"). La violazione della concorrenza consente di essere informati del mancato aggiornamento.The concurrency violation simply lets you know that the update failed. È quindi necessario decidere se sovrascrivere le modifiche apportate dall'Utente2 con le modifiche apportate dall'Utente1 o annullare le modifiche dell'Utente1.The decision now needs to be made whether to overwrite the changes supplied by User2 with the changes supplied by User1, or to cancel the changes by User1.

Verifica di violazioni alla concorrenza ottimisticaTesting for Optimistic Concurrency Violations

Per verificare eventuali violazioni alla concorrenza ottimistica, sono disponibili diverse tecniche.There are several techniques for testing for an optimistic concurrency violation. Una di queste prevede l'inclusione di una colonna timestamp nella tabella.One involves including a timestamp column in the table. In genere, la funzionalità timestamp, che consente di identificare la data e l'ora dell'ultimo aggiornamento apportato al record, viene fornita dai database.Databases commonly provide timestamp functionality that can be used to identify the date and time when the record was last updated. Se si usa questa tecnica, una colonna timestamp viene inclusa nella definizione della tabella.Using this technique, a timestamp column is included in the table definition. Il timestamp viene aggiornato a ogni aggiornamento del record, in modo da riflettere la data e l'ora correnti.Whenever the record is updated, the timestamp is updated to reflect the current date and time. Quando si esegue una verifica di eventuali violazioni alla concorrenza ottimistica, la colonna timestamp viene restituita con una query relativa ai contenuti della tabella.In a test for optimistic concurrency violations, the timestamp column is returned with any query of the contents of the table. A ogni tentativo di aggiornamento, il valore timestamp del database viene confrontato con il valore timestamp originale contenuto nella riga modificata.When an update is attempted, the timestamp value in the database is compared to the original timestamp value contained in the modified row. Se tali valori corrispondono, l'aggiornamento viene eseguito e la colonna timestamp viene aggiornata con l'ora corrente, in modo da riflettere l'aggiornamento.If they match, the update is performed and the timestamp column is updated with the current time to reflect the update. Se i valori non corrispondono, si è verificata una violazione della concorrenza ottimistica.If they do not match, an optimistic concurrency violation has occurred.

Un'altra tecnica per rilevare eventuali violazioni alla concorrenza ottimistica consiste nel verificare che tutti i valori di colonna originali corrispondano ancora ai valori presenti nel database.Another technique for testing for an optimistic concurrency violation is to verify that all the original column values in a row still match those found in the database. Si consideri ad esempio la seguente query:For example, consider the following query:

SELECT Col1, Col2, Col3 FROM Table1  

Per verificare eventuali violazioni alla concorrenza ottimistica quando si aggiorna una riga in Table1, è necessario eseguire la seguente istruzione UPDATE:To test for an optimistic concurrency violation when updating a row in Table1, you would issue the following UPDATE statement:

UPDATE Table1 Set Col1 = @NewCol1Value,  
              Set Col2 = @NewCol2Value,  
              Set Col3 = @NewCol3Value  
WHERE Col1 = @OldCol1Value AND  
      Col2 = @OldCol2Value AND  
      Col3 = @OldCol3Value  

Se i valori originali corrispondono ai valori del database, sarà possibile effettuare l'aggiornamento.As long as the original values match the values in the database, the update is performed. Se un valore è stato modificato, la riga non verrà modificata dall'aggiornamento, poiché la clausola WHERE non sarà in grado di trovare alcuna corrispondenza.If a value has been modified, the update will not modify the row because the WHERE clause will not find a match.

Notare che è sempre consigliabile restituire un valore univoco di chiave primaria nella query.Note that it is recommended to always return a unique primary key value in your query. In caso contrario, è possibile che la precedente istruzione UPDATE provochi l'aggiornamento non desiderato di più righe.Otherwise, the preceding UPDATE statement may update more than one row, which might not be your intent.

Se in una colonna dell'origine dati sono consentiti valori null, è necessario estendere la clausola WHERE, in modo che ricerchi un riferimento null corrispondente nella tabella locale e nell'origine dati.If a column at your data source allows nulls, you may need to extend your WHERE clause to check for a matching null reference in your local table and at the data source. La seguente istruzione UPDATE, ad esempio, consente di verificare la corrispondenza tra un riferimento null nella riga locale e un riferimento null nell'origine dati o la corrispondenza tra un valore nella riga locale e il valore nell'origine dati.For example, the following UPDATE statement verifies that a null reference in the local row still matches a null reference at the data source, or that the value in the local row still matches the value at the data source.

UPDATE Table1 Set Col1 = @NewVal1  
  WHERE (@OldVal1 IS NULL AND Col1 IS NULL) OR Col1 = @OldVal1  

Quando si usa il modello di concorrenza ottimistica, è possibile applicare anche criteri meno restrittivi.You may also choose to apply less restrictive criteria when using an optimistic concurrency model. Ad esempio, se si usano solo le colonne di chiave primaria nella clausola WHERE, i dati verranno sovrascritti, indipendentemente dagli eventuali aggiornamenti apportati alle altre colonne successivamente all'ultima query.For example, using only the primary key columns in the WHERE clause causes the data to be overwritten regardless of whether the other columns have been updated since the last query. È inoltre possibile applicare una clausola WHERE solo a colonne specifiche, in modo che i dati vengano sovrascritti a meno che particolari campi non siano stati aggiornati successivamente all'ultima query.You can also apply a WHERE clause only to specific columns, resulting in data being overwritten unless particular fields have been updated since they were last queried.

Evento DataAdapter.RowUpdatedThe DataAdapter.RowUpdated Event

Il RowUpdated eventi del DataAdapter oggetto può essere utilizzato in combinazione con le tecniche descritte in precedenza, per fornire la notifica all'applicazione di eventuali violazioni alla concorrenza ottimistica.The RowUpdated event of the DataAdapter object can be used in conjunction with the techniques described earlier, to provide notification to your application of optimistic concurrency violations. RowUpdated si verifica dopo ogni tentativo di aggiornare una Modified riga da una DataSet.RowUpdated occurs after each attempt to update a Modified row from a DataSet. È quindi possibile aggiungere un particolare codice di gestione, che includa l'elaborazione nel caso in cui si verifichi un'eccezione, l'aggiunta di informazioni personalizzate relative agli errori, l'aggiunta di logica relativa ai nuovi tentativi e così via.This enables you to add special handling code, including processing when an exception occurs, adding custom error information, adding retry logic, and so on. Il RowUpdatedEventArgs oggetto restituisce un RecordsAffected proprietà contenente il numero di righe interessate da un particolare comando di aggiornamento per una riga modificata in una tabella.The RowUpdatedEventArgs object returns a RecordsAffected property containing the number of rows affected by a particular update command for a modified row in a table. Impostando il comando update per verificare la concorrenza ottimistica, la RecordsAffected proprietà, di conseguenza, restituirà un valore pari a 0 quando si è verificata una violazione della concorrenza ottimistica, poiché nessun record sono stato aggiornato.By setting the update command to test for optimistic concurrency, the RecordsAffected property will, as a result, return a value of 0 when an optimistic concurrency violation has occurred, because no records were updated. In questo caso viene generata un'eccezione.If this is the case, an exception is thrown. Il RowUpdated evento consente di gestire questa occorrenza ed evitare l'eccezione impostando un appropriato RowUpdatedEventArgs valore, come SkipCurrentRow.The RowUpdated event enables you to handle this occurrence and avoid the exception by setting an appropriate RowUpdatedEventArgs.Status value, such as UpdateStatus.SkipCurrentRow. Per altre informazioni sul RowUpdated eventi, vedere gestione degli eventi DataAdapter.For more information about the RowUpdated event, see Handling DataAdapter Events.

Facoltativamente, è possibile impostare DataAdapter. ContinueUpdateOnError al true, prima di chiamare Updatee rispondere alle informazioni di errore archiviate nel RowError proprietà di una particolare riga quando la Update viene completata.Optionally, you can set DataAdapter.ContinueUpdateOnError to true, before calling Update, and respond to the error information stored in the RowError property of a particular row when the Update is completed. Per altre informazioni, vedere le informazioni sull'errore di riga.For more information, see Row Error Information.

Esempio di concorrenza ottimisticaOptimistic Concurrency Example

Di seguito è riportato un esempio semplice che consente di impostare il UpdateCommand di un DataAdapter per eseguire il test della concorrenza ottimistica e quindi Usa il RowUpdated dell'evento di test per violazioni alla concorrenza ottimistica.The following is a simple example that sets the UpdateCommand of a DataAdapter to test for optimistic concurrency, and then uses the RowUpdated event to test for optimistic concurrency violations. Quando viene rilevata una violazione della concorrenza ottimistica, l'applicazione imposta il RowError della riga in cui l'aggiornamento sia stato rilasciato per riflettere una violazione della concorrenza ottimistica.When an optimistic concurrency violation is encountered, the application sets the RowError of the row that the update was issued for to reflect an optimistic concurrency violation.

Si noti che i valori dei parametri passati alla clausola WHERE del comando di aggiornamento vengono eseguito il mapping per il originale i valori delle rispettive colonne.Note that the parameter values passed to the WHERE clause of the UPDATE command are mapped to the Original values of their respective columns.

' Assumes connection is a valid SqlConnection.  
Dim adapter As SqlDataAdapter = New SqlDataAdapter( _  
  "SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID", _  
  connection)  

' The Update command checks for optimistic concurrency violations  
' in the WHERE clause.  
adapter.UpdateCommand = New SqlCommand("UPDATE Customers " &  
  "(CustomerID, CompanyName) VALUES(@CustomerID, @CompanyName) " & _  
  "WHERE CustomerID = @oldCustomerID AND CompanyName = " &  
  "@oldCompanyName", connection)  
adapter.UpdateCommand.Parameters.Add( _  
  "@CustomerID", SqlDbType.NChar, 5, "CustomerID")  
adapter.UpdateCommand.Parameters.Add( _  
  "@CompanyName", SqlDbType.NVarChar, 30, "CompanyName")  

' Pass the original values to the WHERE clause parameters.  
Dim parameter As SqlParameter = dataSet.UpdateCommand.Parameters.Add( _  
  "@oldCustomerID", SqlDbType.NChar, 5, "CustomerID")  
parameter.SourceVersion = DataRowVersion.Original  
parameter = adapter.UpdateCommand.Parameters.Add( _  
  "@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName")  
parameter.SourceVersion = DataRowVersion.Original  

' Add the RowUpdated event handler.  
AddHandler adapter.RowUpdated, New SqlRowUpdatedEventHandler( _  
  AddressOf OnRowUpdated)  

Dim dataSet As DataSet = New DataSet()  
adapter.Fill(dataSet, "Customers")  

' Modify the DataSet contents.  
adapter.Update(dataSet, "Customers")  

Dim dataRow As DataRow  

For Each dataRow In dataSet.Tables("Customers").Rows  
    If dataRow.HasErrors Then   
       Console.WriteLine(dataRow (0) & vbCrLf & dataRow.RowError)  
    End If  
Next  

Private Shared Sub OnRowUpdated( _  
  sender As object, args As SqlRowUpdatedEventArgs)  
   If args.RecordsAffected = 0  
      args.Row.RowError = "Optimistic Concurrency Violation!"  
      args.Status = UpdateStatus.SkipCurrentRow  
   End If  
End Sub  
// Assumes connection is a valid SqlConnection.  
SqlDataAdapter adapter = new SqlDataAdapter(  
  "SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID",  
  connection);  

// The Update command checks for optimistic concurrency violations  
// in the WHERE clause.  
adapter.UpdateCommand = new SqlCommand("UPDATE Customers Set CustomerID = @CustomerID, CompanyName = @CompanyName " +  
   "WHERE CustomerID = @oldCustomerID AND CompanyName = @oldCompanyName", connection);  
adapter.UpdateCommand.Parameters.Add(  
  "@CustomerID", SqlDbType.NChar, 5, "CustomerID");  
adapter.UpdateCommand.Parameters.Add(  
  "@CompanyName", SqlDbType.NVarChar, 30, "CompanyName");  

// Pass the original values to the WHERE clause parameters.  
SqlParameter parameter = adapter.UpdateCommand.Parameters.Add(  
  "@oldCustomerID", SqlDbType.NChar, 5, "CustomerID");  
parameter.SourceVersion = DataRowVersion.Original;  
parameter = adapter.UpdateCommand.Parameters.Add(  
  "@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName");  
parameter.SourceVersion = DataRowVersion.Original;  

// Add the RowUpdated event handler.  
adapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);  

DataSet dataSet = new DataSet();  
adapter.Fill(dataSet, "Customers");  

// Modify the DataSet contents.  

adapter.Update(dataSet, "Customers");  

foreach (DataRow dataRow in dataSet.Tables["Customers"].Rows)  
{  
    if (dataRow.HasErrors)  
       Console.WriteLine(dataRow [0] + "\n" + dataRow.RowError);  
}  

protected static void OnRowUpdated(object sender, SqlRowUpdatedEventArgs args)  
{  
  if (args.RecordsAffected == 0)   
  {  
    args.Row.RowError = "Optimistic Concurrency Violation Encountered";  
    args.Status = UpdateStatus.SkipCurrentRow;  
  }  
}  

Vedere ancheSee Also

Recupero e modifica di dati in ADO.NETRetrieving and Modifying Data in ADO.NET
Aggiornamento di origini dati con DataAdapterUpdating Data Sources with DataAdapters
Informazioni sugli errori di rigaRow Error Information
Transazioni e concorrenzaTransactions and Concurrency
Provider gestiti ADO.NET e Centro per sviluppatori di set di datiADO.NET Managed Providers and DataSet Developer Center