Gestione della concorrenza di Sql transtrione

Supponiamo, sto per avviare un progetto utilizzando ASP.NET e SQL Server 2005. Devo progettare il requisito di concorrenza per questa applicazione. Sto pianificando di aggiungere una colonna TimeStamp in ogni tabella. Durante l’aggiornamento delle tabelle, verificherò che la colonna TimeStamp sia uguale a quella selezionata.

Questo approccio sarà sufficiente? O ci sono carenze per questo approccio in qualsiasi circostanza?

Per favore consiglio

Grazie

Lijo

Prima di tutto il modo in cui descrivi nella tua domanda è secondo me il modo migliore per l’applicazione ASP.NET con MS SQL come database. Non c’è nessun blocco nel database. È perfetto con client permanentemente disconnessi come i client web.

Come si può leggere da alcune risposte, c’è un equivoco nella terminologia. Tutti intendiamo utilizzare Microsoft SQL Server 2008 o versione successiva per contenere il database. Se apri nella documentazione di MS SQL Server 2008 l’argomento “rowversion (Transact-SQL)” troverai:

timestamp è il sinonimo per il tipo di dati rowversion ed è sobject al comportamento del sinonimo del tipo di dati.” … “La syntax del timestamp è deprecata, questa funzionalità verrà rimossa in una versione futura di Microsoft SQL Server. Evitare di utilizzare questa funzione in un nuovo lavoro di sviluppo e pianificare di modificare le applicazioni che attualmente utilizzano questa funzione.”

Quindi il tipo di dati timestamp è il sinonimo del tipo di dati rowversion per MS SQL. Contiene 64 bit il contatore che esiste internamente in ogni database e può essere visto come @@ DBTS . Dopo una modifica di una riga in una tabella del database, il contatore verrà incrementato.

Mentre leggo la tua domanda, leggo “TimeStamp” come nome di colonna dei dati di tipo rowversion . Personalmente preferisco il nome RowUpdateTimeStamp . In AzManDB (vedere Microsoft Authorization Manager con l’archivio come DB) ho potuto vedere tale nome. A volte sono stati utilizzati anche ChildUpdateTimeStamp per tracciare strutture gerarchiche di RowUpdateTimeStamp (rispetto ai trigger).

Ho implementato questo approccio nel mio ultimo progetto e sono molto felice. In genere fai quanto segue:

  1. Aggiungi la colonna RowUpdateTimeStam p a ogni tabella del tuo database con il tipo rowversion (verrà visualizzato in Microsoft SQL Management Studio come timestamp , che è lo stesso).
  2. È necessario creare tutte le query SQL SELECT per l’invio dei risultati al client, in modo da inviare un valore RowVersion aggiuntivo insieme ai dati principali. Se hai una SELECT con JOINT, dovresti inviare RowVersion del valore massimo di RowUpdateTimeStamp da entrambe le tabelle come
SELECT s.Id AS Id ,s.Name AS SoftwareName ,m.Name AS ManufacturerName ,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp THEN s.RowUpdateTimeStamp ELSE m.RowUpdateTimeStamp END AS RowUpdateTimeStamp FROM dbo.Software AS s INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id 

Oppure fai un casting di dati come segue

 SELECT s.Id AS Id ,s.Name AS SoftwareName ,m.Name AS ManufacturerName ,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp THEN CAST(s.RowUpdateTimeStamp AS bigint) ELSE CAST(m.RowUpdateTimeStamp AS bigint) END AS RowUpdateTimeStamp FROM dbo.Software AS s INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id 

per mantenere RowUpdateTimeStamp come bigint , che corrisponde al tipo di dati di C #. Se rendi OUTER JOINT o JOINTs da molte tabelle, il costrutto MAX(RowUpdateTimeStamp) da tutte le tabelle sarà visto un po ‘più complesso. Poiché MS SQL non supporta funzioni come MAX (a, b, c, d, e), il costrutto corrispondente potrebbe avere il seguente aspetto:

 (SELECT MAX(rv) FROM (SELECT table1.RowUpdateTimeStamp AS rv UNION ALL SELECT table2.RowUpdateTimeStamp UNION ALL SELECT table3.RowUpdateTimeStamp UNION ALL SELECT table4.RowUpdateTimeStamp UNION ALL SELECT table5.RowUpdateTimeStamp) AS maxrv) AS RowUpdateTimeStamp 
  1. Tutti i client disconnessi (client Web) ricevono e mantengono non solo alcune righe di dati, ma RowVersion (tipo ulong ) della riga di dati.
  2. In un tentativo di modificare i dati dal client disconnesso, il client deve inviare RowVersion corrisponde ai dati originali sul server. La procedura memorizzata spSoftwareUpdate potrebbe essere simile
 CREATE PROCEDURE dbo.spSoftwareUpdate @Id int, @SoftwareName varchar(100), @originalRowUpdateTimeStamp bigint, -- used for optimistic concurrency mechanism @NewRowUpdateTimeStamp bigint OUTPUT AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. -- ExecuteNonQuery() returns -1, but it is not an error -- one should test @NewRowUpdateTimeStamp for DBNull SET NOCOUNT ON; UPDATE dbo.Software SET Name = @SoftwareName WHERE Id = @Id AND RowUpdateTimeStamp <= @originalRowUpdateTimeStamp SET @NewRowUpdateTimeStamp = (SELECT RowUpdateTimeStamp FROM dbo.Software WHERE (@@ROWCOUNT > 0) AND (Id = @Id)); END 

Codice di dbo.spSoftwareDelete stored procedure appare come la stessa. Se non si triggers NOCOUNT , è ansible generare DBConcurrencyException generato automaticamente in molti scenari. Visual Studio offre la possibilità di utilizzare la concorrenza ottimistica come “Usa concorrenza ottimistica” nelle Opzioni avanzate di TableAdapter o DataAdapter .

Se si guarda la stored procedure di dbo.spSoftwareUpdate carful, troverete che uso RowUpdateTimeStamp <= @originalRowUpdateTimeStamp in WHERE invece di RowUpdateTimeStamp = @originalRowUpdateTimeStamp . Lo faccio perché, il valore di @originalRowUpdateTimeStamp che ha il client in genere sono costruiti come MAX(RowUpdateTimeStamp) da più come una tabella. Quindi può essere che RowUpdateTimeStamp < @originalRowUpdateTimeStamp . O dovresti usare rigorosa uguaglianza = e riprodurre qui la stessa istruzione complessa JOIN che hai usato nell'istruzione SELECT o usare <= costruisci come me e mantenere la stessa sicurezza di prima.

A proposito, si può build un ottimo valore per ETag basato su RowUpdateTimeStamp che può essere inviato nell'intestazione HTTP al client insieme ai dati. Con ETag è ansible implementare la memorizzazione intelligente dei dati sul lato client.

Non riesco a scrivere tutto il codice qui, ma puoi trovare molti esempi su Internet. Voglio solo ripetere ancora una volta che, a mio parere, l'utilizzo della concorrenza ottimistica basata su rowversion è il modo migliore per la maggior parte degli scenari ASP.NET .

In SQL Server un approccio consigliato per il tipo di situazione consiste nel creare una colonna di tipo ‘rowversion’ e utilizzarla per verificare se uno qualsiasi dei campi in quella riga è stato modificato.

SQL Server garantisce che se qualsiasi valore nella riga cambia (o viene inserita una nuova riga) la colonna di rowversion verrà automaticamente aggiornata a un valore diverso. Lasciare che il database gestisca questo per te è molto più affidabile di provare a farlo da solo.

Nelle istruzioni di aggiornamento è sufficiente aggiungere una clausola where per verificare che il valore di rowversion sia lo stesso di quando è stata richiamata per la prima volta. In caso contrario, qualcun altro ha cambiato la riga (es: è sporca)

Inoltre, da quella pagina:

La syntax del timestamp è deprecata. Questa funzionalità verrà rimossa in una versione futura di Microsoft SQL Server. Evitare l’uso di questa funzione in un nuovo lavoro di sviluppo e pianificare di modificare le applicazioni che attualmente utilizzano questa funzione.

Non sono sicuro che la concorrenza debba essere gestita nel database come questo. Il database stesso dovrebbe essere in grado di gestire l’isolamento e il comportamento transazionale, ma il comportamento del threading dovrebbe essere fatto nel codice.

suggerimento di rowversion è corretto direi ma è deludente vedere che il timestamp sarà presto ritirato. Alcune delle mie applicazioni OLD lo utilizzano per motivi diversi, quindi controllano la concorrenza.