Implementazione della concorrenza ottimistica con SqlDataSource (C#)

di Scott Mitchell

Scarica il PDF

In questa esercitazione vengono esaminati i concetti fondamentali del controllo della concorrenza ottimistica e quindi viene illustrato come implementarlo usando il controllo SqlDataSource.

Introduzione

Nell'esercitazione precedente è stato illustrato come aggiungere funzionalità di inserimento, aggiornamento ed eliminazione al controllo SqlDataSource. In breve, per fornire queste funzionalità è necessario specificare l'istruzione , UPDATEo DELETE SQL corrispondente INSERTnelle proprietà , UpdateCommando del controllo , InsertCommando DeleteCommand , insieme ai parametri appropriati nelle InsertParametersraccolte , UpdateParameterse DeleteParameters . Anche se queste proprietà e raccolte possono essere specificate manualmente, il pulsante Avanzate della Procedura guidata Configura origine dati offre una casella di controllo Genera INSERTistruzioni , UPDATEe DELETE che creerà automaticamente queste istruzioni in base all'istruzione SELECT .

Insieme alla casella di controllo Genera INSERTistruzioni , UPDATEe DELETE , la finestra di dialogo Opzioni avanzate di generazione SQL include un'opzione Usa concorrenza ottimistica (vedere la figura 1). Se selezionata, le WHERE clausole nella generazione UPDATE automatica e DELETE le istruzioni vengono modificate per eseguire l'aggiornamento o l'eliminazione solo se i dati del database sottostanti non sono stati modificati dall'ultimo caricamento dei dati nella griglia.

È possibile aggiungere il supporto della concorrenza ottimistica dalla finestra di dialogo Opzioni avanzate di generazione SQL

Figura 1: È possibile aggiungere il supporto della concorrenza ottimistica dalla finestra di dialogo Opzioni avanzate di generazione SQL

Nell'esercitazione Implementazione della concorrenza ottimistica sono stati esaminati i concetti fondamentali del controllo della concorrenza ottimistica e come aggiungerlo a ObjectDataSource. In questa esercitazione verranno ritoccati gli elementi essenziali del controllo della concorrenza ottimistica e quindi si esaminerà come implementarlo usando SqlDataSource.

Riepilogo della concorrenza ottimistica

Per le applicazioni Web che consentono a più utenti simultanei di modificare o eliminare gli stessi dati, esiste la possibilità che un utente sovrascriva accidentalmente altre modifiche. Nell'esercitazione Implementazione della concorrenza ottimistica ho fornito l'esempio seguente:

Si supponga che due utenti, Jisun e Sam, visitassero entrambe una pagina in un'applicazione che consentiva ai visitatori di aggiornare ed eliminare i prodotti tramite un controllo GridView. Entrambi fare clic sul pulsante Modifica per Chai nello stesso momento. Jisun modifica il nome del prodotto in Chai Tea e fa clic sul pulsante Aggiorna. Il risultato netto è un'istruzione UPDATE inviata al database, che imposta tutti i campi aggiornabili del prodotto (anche se Jisun ha aggiornato un solo campo, ProductName). A questo punto, il database ha i valori Chai Tea, la categoria Beverages, il fornitore Esotico Liquids e così via per questo particolare prodotto. Tuttavia, la schermata GridView in Sam mostra ancora il nome del prodotto nella riga GridView modificabile come Chai. Dopo il commit delle modifiche di Jisun, Sam aggiorna la categoria a Condimenti e fa clic su Aggiorna. Viene restituita un'istruzione UPDATE inviata al database che imposta il nome del prodotto su Chai, sull'ID CategoryID categoria Condiments corrispondente e così via. Le modifiche di Jisun al nome del prodotto sono state sovrascritte.

La figura 2 illustra questa interazione.

Quando due utenti aggiornano simultaneamente un record, è possibile che un utente modi di sovrascrivere l'altro

Figura 2: Quando due utenti aggiornano simultaneamente un record è possibile che un utente modichi per sovrascrivere l'altro (fare clic per visualizzare l'immagine a dimensione intera)

Per impedire lo svolgimento di questo scenario, è necessario implementare una forma di controllo della concorrenza . La concorrenza ottimistica che si concentra su questa esercitazione si basa sul presupposto che, sebbene ci siano conflitti di concorrenza ogni ora e poi, la maggior parte del tempo tali conflitti non si verificheranno. Pertanto, se si verifica un conflitto, il controllo della concorrenza ottimistica informa semplicemente l'utente che le modifiche non possono essere salvate perché un altro utente ha modificato gli stessi dati.

Nota

Per le applicazioni in cui si presuppone che ci saranno molti conflitti di concorrenza o se tali conflitti non sono tollerabili, è possibile usare invece un controllo di concorrenza pessimistico. Fare riferimento all'esercitazione Implementazione della concorrenza ottimistica per una discussione più approfondita sul controllo della concorrenza pessimistica.

Il controllo della concorrenza ottimistica funziona assicurandosi che il record da aggiornare o eliminare abbia gli stessi valori di quando è stato avviato il processo di aggiornamento o eliminazione. Ad esempio, quando si fa clic sul pulsante Modifica in un controllo GridView modificabile, i valori dei record vengono letti dal database e visualizzati in Caselle di testo e in altri controlli Web. Questi valori originali vengono salvati da GridView. Successivamente, dopo che l'utente apporta le modifiche e fa clic sul pulsante Aggiorna, l'istruzione UPDATE utilizzata deve tenere conto dei valori originali più i nuovi valori e aggiornare il record del database sottostante solo se i valori originali che l'utente ha iniziato a modificare sono identici ai valori ancora presenti nel database. La figura 3 illustra questa sequenza di eventi.

Affinché l'aggiornamento o l'eliminazione abbia esito positivo, i valori originali devono essere uguali ai valori del database corrente

Figura 3: Per eseguire l'aggiornamento o l'eliminazione, i valori originali devono essere uguali ai valori del database corrente (fare clic per visualizzare l'immagine a dimensione intera)

Esistono vari approcci all'implementazione della concorrenza ottimistica (vedere La logica di aggiornamento ottimistico della concorrenza ottimistica di Peter A. Kuberneberg per una breve panoramica delle opzioni). La tecnica usata da SqlDataSource (nonché dai set di dati tipizzati di ADO.NET usati nel livello di accesso ai dati) aumenta la WHERE clausola per includere un confronto di tutti i valori originali. L'istruzione seguente UPDATE , ad esempio, aggiorna il nome e il prezzo di un prodotto solo se i valori del database correnti sono uguali ai valori recuperati originariamente durante l'aggiornamento del record in GridView. I @ProductName parametri e @UnitPrice contengono i nuovi valori immessi dall'utente, mentre @original_ProductName e @original_UnitPrice contengono i valori originariamente caricati in GridView quando si fa clic sul pulsante Modifica:

UPDATE Products SET
    ProductName = @ProductName,
    UnitPrice = @UnitPrice
WHERE
    ProductID = @original_ProductID AND
    ProductName = @original_ProductName AND
    UnitPrice = @original_UnitPrice

Come si vedrà in questa esercitazione, l'abilitazione del controllo della concorrenza ottimistica con SqlDataSource è semplice come selezionare una casella di controllo.

Passaggio 1: Creazione di un oggetto SqlDataSource che supporta la concorrenza ottimistica

Per iniziare, aprire la OptimisticConcurrency.aspx pagina dalla SqlDataSource cartella . Trascinare un controllo SqlDataSource dalla casella degli strumenti nella Designer, impostare la relativa ID proprietà su ProductsDataSourceWithOptimisticConcurrency. Fare quindi clic sul collegamento Configura origine dati dallo smart tag del controllo. Nella prima schermata della procedura guidata scegliere di usare NORTHWINDConnectionString e fare clic su Avanti.

Scegliere di usare NORTHWINDConnectionString

Figura 4: Scegliere di usare (fare clic per visualizzare l'immagineNORTHWINDConnectionString a dimensione intera)

Per questo esempio si aggiungerà un controllo GridView che consente agli utenti di modificare la Products tabella. Di conseguenza, nella schermata Configura istruzione seleziona, scegliere la Products tabella dall'elenco a discesa e selezionare le ProductIDcolonne , ProductName, UnitPricee Discontinued , come illustrato nella figura 5.

Dalla tabella Products restituire le colonne ProductID, ProductName, UnitPrice e Discontinued

Figura 5: Dalla Products tabella, restituire le ProductIDcolonne , ProductName, UnitPricee Discontinued (fare clic per visualizzare l'immagine a dimensione intera)

Dopo aver selezionato le colonne, fare clic sul pulsante Avanzate per visualizzare la finestra di dialogo Opzioni avanzate di generazione SQL. Selezionare le caselle di controllo Genera INSERTistruzioni , UPDATEe DELETE Usa concorrenza ottimistica e fare clic su OK (fare riferimento alla figura 1 per uno screenshot). Completare la procedura guidata facendo clic su Avanti, quindi su Fine.

Dopo aver completato la procedura guidata Configura origine dati, esaminare le proprietà e risultanti DeleteCommand e le DeleteParameters raccolte e UpdateParameters .UpdateCommand Il modo più semplice per eseguire questa operazione consiste nel fare clic sulla scheda Origine nell'angolo inferiore sinistro per visualizzare la sintassi dichiarativa della pagina. Qui troverai un UpdateCommand valore di:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued

Con sette parametri nella UpdateParameters raccolta:

<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ...>
    <DeleteParameters>
      ...
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </UpdateParameters>
    ...
</asp:SqlDataSource>

Analogamente, la proprietà e DeleteParameters la DeleteCommand raccolta dovrebbero essere simili alle seguenti:

DELETE FROM [Products]
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ...>
    <DeleteParameters>
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        ...
    </UpdateParameters>
    ...
</asp:SqlDataSource>

Oltre ad aumentare le WHERE clausole delle UpdateCommand proprietà e DeleteCommand (e aggiungere i parametri aggiuntivi alle rispettive raccolte di parametri), selezionando l'opzione Usa concorrenza ottimistica si modificano due altre proprietà:

Quando il controllo Web dei dati richiama il metodo o Delete() sqlDataSourceUpdate(), passa i valori originali. Se la proprietà sqlDataSource s ConflictDetection è impostata su CompareAllValues, questi valori originali vengono aggiunti al comando. La OldValuesParameterFormatString proprietà fornisce il modello di denominazione usato per questi parametri di valore originale. La procedura guidata Configura origine dati usa original_{0} e assegna un nome a ogni parametro originale nelle DeleteCommandUpdateCommand proprietà e nelle raccolte e UpdateParametersDeleteParameters di conseguenza.

Nota

Poiché non si usano le funzionalità di inserimento del controllo SqlDataSource, è possibile rimuovere la proprietà e la InsertCommand relativa InsertParameters raccolta.

Gestione corretta deiNULLvalori

Sfortunatamente, le istruzioni e DELETE aumentate UPDATE generate automaticamente dalla procedura guidata Configura origine dati quando si usa la concorrenza ottimistica non funzionano con i record che contengono NULL valori. Per vedere perché, prendere in considerazione sqlDataSource s UpdateCommand:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued

La UnitPrice colonna nella Products tabella può avere NULL valori. Se un determinato record ha un NULL valore per UnitPrice, la parte [UnitPrice] = @original_UnitPrice della WHERE clausola restituirà sempre False perché NULL = NULL restituisce sempre False. Pertanto, i record che contengono NULL valori non possono essere modificati o eliminati, perché le UPDATE clausole e DELETE delle istruzioni WHERE non restituiscono righe da aggiornare o eliminare.

Nota

Questo bug è stato segnalato per la prima volta a Microsoft nel mese di giugno 2004 in SqlDataSource genera istruzioni SQL non corrette e viene segnalato come corretto nella versione successiva di ASP.NET.

Per risolvere questo problema, è necessario aggiornare manualmente le WHERE clausole in entrambe le UpdateCommand proprietà e DeleteCommand per tutte le colonne che possono avere NULL valori. In generale, passare [ColumnName] = @original_ColumnName a:

(
   ([ColumnName] IS NULL AND @original_ColumnName IS NULL)
     OR
   ([ColumnName] = @original_ColumnName)
)

Questa modifica può essere eseguita direttamente tramite il markup dichiarativo, tramite le opzioni UpdateQuery o DeleteQuery dal Finestra Proprietà oppure tramite le schede UPDATE e DELETE nell'opzione Specificare un'istruzione SQL personalizzata o una stored procedure nella procedura guidata Configura origine dati. Anche in questo caso, questa modifica deve essere apportata per ogni colonna nella UpdateCommand clausola e DeleteCommand s WHERE che può contenere NULL valori.

Applicando questo valore all'esempio vengono restituiti i valori e DeleteCommand modificati UpdateCommand seguenti:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
        OR ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued
DELETE FROM [Products]
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
        OR ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued

Passaggio 2: Aggiunta di un controllo GridView con opzioni di modifica ed eliminazione

Con SqlDataSource configurato per supportare la concorrenza ottimistica, tutto ciò che rimane consiste nell'aggiungere un controllo Web dati alla pagina che usa questo controllo di concorrenza. Per questa esercitazione, è possibile aggiungere un controllo GridView che fornisce funzionalità di modifica ed eliminazione. A tale scopo, trascinare un controllo GridView dalla casella degli strumenti nella Designer e impostarlo ID su Products. Dallo smart tag gridView, associarlo al ProductsDataSourceWithOptimisticConcurrency controllo SqlDataSource aggiunto al passaggio 1. Infine, selezionare le opzioni Abilita modifica e Abilita eliminazione dallo smart tag.

Associare GridView a SqlDataSource e abilitare la modifica e l'eliminazione

Figura 6: Associare GridView a SqlDataSource e abilitare la modifica e l'eliminazione (fare clic per visualizzare l'immagine a dimensione intera)

Dopo aver aggiunto GridView, configurarne l'aspetto rimuovendo BoundField ProductID , modificando la ProductName proprietà BoundField in HeaderText Product e aggiornando BoundField UnitPrice in modo che la relativa HeaderText proprietà sia semplicemente Price. Idealmente, si migliora l'interfaccia di modifica per includere un RequiredFieldValidator per il ProductName valore e un CompareValidator per il UnitPrice valore (per assicurarsi che sia un valore numerico formattato correttamente). Vedere l'esercitazione Personalizzazione dell'interfaccia di modifica dei dati per un'analisi più approfondita della personalizzazione dell'interfaccia di modifica di GridView.

Nota

Lo stato di visualizzazione di GridView deve essere abilitato perché i valori originali passati da GridView a SqlDataSource vengono archiviati nello stato di visualizzazione.

Dopo aver apportato queste modifiche a GridView, il markup dichiarativo GridView e SqlDataSource dovrebbe essere simile al seguente:

<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ConflictDetection="CompareAllValues"
    ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
    DeleteCommand=
        "DELETE FROM [Products]
         WHERE [ProductID] = @original_ProductID
         AND [ProductName] = @original_ProductName
         AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
              OR ([UnitPrice] = @original_UnitPrice))
         AND [Discontinued] = @original_Discontinued"
    OldValuesParameterFormatString=
        "original_{0}"
    SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued]
         FROM [Products]"
    UpdateCommand=
        "UPDATE [Products]
         SET [ProductName] = @ProductName, [UnitPrice] = @UnitPrice,
            [Discontinued] = @Discontinued
         WHERE [ProductID] = @original_ProductID
         AND [ProductName] = @original_ProductName
         AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
            OR ([UnitPrice] = @original_UnitPrice))
        AND [Discontinued] = @original_Discontinued">
    <DeleteParameters>
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </UpdateParameters>
</asp:SqlDataSource>
<asp:GridView ID="Products" runat="server"
    AutoGenerateColumns="False" DataKeyNames="ProductID"
    DataSourceID="ProductsDataSourceWithOptimisticConcurrency">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" HeaderText="Price"
            SortExpression="UnitPrice" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>

Per visualizzare il controllo della concorrenza ottimistica in azione, aprire due finestre del browser e caricare la OptimisticConcurrency.aspx pagina in entrambi. Fare clic sui pulsanti Modifica per il primo prodotto in entrambi i browser. In un browser modificare il nome del prodotto e fare clic su Aggiorna. Il browser eseguirà il postback e GridView tornerà alla modalità di pre-modifica, mostrando il nuovo nome del prodotto per il record appena modificato.

Nella seconda finestra del browser modificare il prezzo (ma lasciare il nome del prodotto come valore originale) e fare clic su Aggiorna. Al postback, la griglia torna alla modalità di pre-modifica, ma la modifica al prezzo non viene registrata. Il secondo browser mostra lo stesso valore del primo nome del nuovo prodotto con il prezzo precedente. Le modifiche apportate nella seconda finestra del browser sono andate perse. Inoltre, le modifiche sono state perse in modo piuttosto silenzioso, poiché non vi sono state eccezioni o messaggi che indicano che si è appena verificata una violazione della concorrenza.

Le modifiche nella seconda finestra del browser sono state perse automaticamente

Figura 7: Le modifiche nella seconda finestra del browser sono state perse automaticamente (fare clic per visualizzare l'immagine a dimensione intera)

Il motivo per cui non è stato eseguito il commit delle modifiche apportate al secondo browser è stato dovuto al fatto che la clausola dell'istruzione UPDATE ha WHERE filtrato tutti i record e pertanto non ha effetto sulle righe. Esaminiamo di nuovo l'istruzione UPDATE :

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL) OR
        ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued

Quando la seconda finestra del browser aggiorna il record, il nome del prodotto originale specificato nella WHERE clausola non corrisponde al nome del prodotto esistente (poiché è stato modificato dal primo browser). Pertanto, l'istruzione [ProductName] = @original_ProductName restituisce False e non UPDATE influisce sui record.

Nota

L'eliminazione funziona nello stesso modo. Con due finestre del browser aperte, iniziare modificando un determinato prodotto con uno e quindi salvandone le modifiche. Dopo aver salvato le modifiche in un browser, fare clic sul pulsante Elimina per lo stesso prodotto nell'altro. Poiché i valori originali non corrispondono nella clausola dell'istruzione DELETE , WHERE l'eliminazione non riesce automaticamente.

Dal punto di vista dell'utente finale nella seconda finestra del browser, dopo aver fatto clic sul pulsante Aggiorna, la griglia torna alla modalità di pre-modifica, ma le modifiche sono andate perse. Tuttavia, non c'è alcun feedback visivo che le modifiche non sono state attaccate. Idealmente, se le modifiche apportate da un utente vengono perse a una violazione della concorrenza, è possibile inviare una notifica e, forse, mantenere la griglia in modalità di modifica. Esaminiamo come eseguire questa operazione.

Passaggio 3: Determinare quando si è verificata una violazione della concorrenza

Poiché una violazione della concorrenza rifiuta le modifiche apportate, sarebbe bello avvisare l'utente quando si è verificata una violazione della concorrenza. Per avvisare l'utente, è possibile aggiungere un controllo Web Etichetta nella parte superiore della pagina denominata ConcurrencyViolationMessage la cui Text proprietà visualizza il messaggio seguente: Si è tentato di aggiornare o eliminare un record che è stato aggiornato contemporaneamente da un altro utente. Esaminare le modifiche dell'altro utente e quindi ripetere l'aggiornamento o l'eliminazione. Impostare la proprietà S del CssClass controllo Label su Warning, ovvero una classe CSS definita in Styles.css che visualizza il testo in un tipo di carattere rosso, corsivo, grassetto e grande. Impostare infine le proprietà Label s Visible e EnableViewState su false. In questo modo l'etichetta verrà nascosta, ad eccezione dei postback in cui la proprietà viene impostata Visible in modo esplicito su true.

Aggiungere un controllo Etichetta alla pagina per visualizzare l'avviso

Figura 8: Aggiungere un controllo Etichetta alla pagina per visualizzare l'avviso (fare clic per visualizzare l'immagine a dimensione intera)

Quando si esegue un aggiornamento o un'eliminazione, i gestori eventi e RowDeleted di RowUpdated GridView vengono attivati dopo che il controllo origine dati ha eseguito l'aggiornamento o l'eliminazione richiesti. È possibile determinare il numero di righe interessate dall'operazione da questi gestori eventi. Se sono state interessate zero righe, si vuole visualizzare l'etichetta ConcurrencyViolationMessage .

Creare un gestore eventi per gli RowUpdated eventi e RowDeleted e aggiungere il codice seguente:

protected void Products_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
    if (e.AffectedRows == 0)
    {
        ConcurrencyViolationMessage.Visible = true;
        e.KeepInEditMode = true;
        // Rebind the data to the GridView to show the latest changes
        Products.DataBind();
    }
}
protected void Products_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
    if (e.AffectedRows == 0)
        ConcurrencyViolationMessage.Visible = true;
}

In entrambi i gestori eventi viene controllata la e.AffectedRows proprietà e, se è uguale a 0, impostare la ConcurrencyViolationMessage proprietà Label s Visible su true. RowUpdated Nel gestore eventi viene inoltre indicato a GridView di rimanere in modalità di modifica impostando la KeepInEditMode proprietà su true. In questo modo, è necessario riassociare i dati alla griglia in modo che gli altri dati dell'utente vengano caricati nell'interfaccia di modifica. Questa operazione viene eseguita chiamando il metodo gridView s DataBind() .

Come illustrato nella figura 9, con questi due gestori eventi, viene visualizzato un messaggio molto evidente ogni volta che si verifica una violazione della concorrenza.

Un messaggio viene visualizzato in caso di violazione della concorrenza

Figura 9: Un messaggio viene visualizzato in faccia a una violazione di concorrenza (fare clic per visualizzare l'immagine a dimensione intera)

Riepilogo

Quando si crea un'applicazione Web in cui più utenti simultanei possono modificare gli stessi dati, è importante considerare le opzioni di controllo della concorrenza. Per impostazione predefinita, i controlli Web ASP.NET dati e i controlli origine dati non usano alcun controllo di concorrenza. Come illustrato in questa esercitazione, l'implementazione del controllo della concorrenza ottimistica con SqlDataSource è relativamente rapida e semplice. SqlDataSource gestisce la maggior parte delle attività per l'aggiunta di clausole aumentate WHERE alle istruzioni e DELETE generate UPDATE automaticamente, ma nella gestione NULL delle colonne dei valori sono presenti alcune sottigliezze, come illustrato nella sezione Gestione corretta dei NULL valori.

Questa esercitazione conclude l'esame di SqlDataSource. Le esercitazioni rimanenti torneranno a usare i dati usando ObjectDataSource e l'architettura a livelli.

Buon programmatori!

Informazioni sull'autore

Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, lavora con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto all'indirizzo mitchell@4GuysFromRolla.com. o tramite il suo blog, disponibile all'indirizzo http://ScottOnWriting.NET.