Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa esercitazione è la prima di quattro che esamina l'aggiornamento, l'eliminazione e l'inserimento di batch di dati. In questa esercitazione si apprenderà come le transazioni di database consentono di eseguire modifiche batch come operazione atomica, assicurando che tutti i passaggi abbiano esito positivo o che tutti i passaggi abbiano esito negativo.
Introduzione
Come abbiamo visto a partire dall'l'esercitazione Panoramica sull'inserimento, l'aggiornamento e l'eliminazione di dati, GridView offre il supporto predefinito per la modifica e l'eliminazione a livello di riga. Con pochi clic del mouse è possibile creare un'interfaccia di modifica dei dati avanzata senza scrivere una riga di codice, purché si sia contenuti con la modifica e l'eliminazione in base a una riga. In alcuni scenari, tuttavia, non è sufficiente e è necessario fornire agli utenti la possibilità di modificare o eliminare un batch di record.
Ad esempio, la maggior parte dei client di posta elettronica basati sul Web usa una griglia per elencare ogni messaggio in cui ogni riga include una casella di controllo insieme alle informazioni del messaggio di posta elettronica (oggetto, mittente e così via). Questa interfaccia consente all'utente di eliminare più messaggi controllandoli e quindi facendo clic su un pulsante Elimina messaggi selezionati. Un'interfaccia di modifica batch è ideale in situazioni in cui gli utenti modificano in genere molti record diversi. Invece di forzare l'utente a fare clic su Modifica, apportare la modifica e quindi fare clic su Aggiorna per ogni record che deve essere modificato, un'interfaccia di modifica batch esegue il rendering di ogni riga con l'interfaccia di modifica. L'utente può modificare rapidamente il set di righe che devono essere modificate e quindi salvare queste modifiche facendo clic su un pulsante Aggiorna tutto. In questo set di esercitazioni si esaminerà come creare interfacce per l'inserimento, la modifica e l'eliminazione di batch di dati.
Quando si eseguono operazioni batch, è importante determinare se è possibile che alcune delle operazioni nel batch abbiano esito positivo mentre altre hanno esito negativo. Si consideri un'interfaccia di eliminazione batch: cosa dovrebbe accadere se il primo record selezionato viene eliminato correttamente, ma il secondo ha esito negativo, ad esempio a causa di una violazione del vincolo di chiave esterna? Il rollback dell'eliminazione del primo record deve essere eseguito o è accettabile che il primo record rimanga eliminato?
Se si vuole che l'operazione batch venga considerata come operazione atomica, una in cui tutti i passaggi hanno esito positivo o tutti i passaggi hanno esito negativo, è necessario aumentare il livello di accesso ai dati per includere il supporto per le transazioni di database. Le transazioni di database garantiscono l'atomicità per il set di istruzioni INSERT, UPDATE e DELETE eseguite nell'ambito della transazione e sono una funzionalità supportata dalla maggior parte dei sistemi di database moderni.
In questo tutorial esploreremo come estendere il DAL per utilizzare le transazioni di database. Le esercitazioni successive esamineranno l'implementazione delle pagine Web per l'inserimento in batch, l'aggiornamento e l'eliminazione di interfacce. Iniziamo!
Annotazioni
Quando si modificano i dati in una transazione batch, l'atomicità non è sempre necessaria. In alcuni scenari, potrebbe essere accettabile che alcune modifiche ai dati abbiano esito positivo e altre nello stesso batch abbiano esito negativo, ad esempio quando si elimina un set di messaggi di posta elettronica da un client di posta elettronica basato sul Web. Se si verifica un errore di database a metà del processo di eliminazione, è probabilmente accettabile che tali record elaborati senza errori rimangano eliminati. In questi casi, il DAL non deve essere modificato per supportare le transazioni di database. Esistono tuttavia altri scenari operativi batch, in cui l'atomicità è fondamentale. Quando un cliente sposta i fondi da un conto bancario a un altro, è necessario eseguire due operazioni: i fondi devono essere detratti dal primo conto e quindi aggiunti al secondo. Anche se la banca potrebbe non avere problemi con il successo del primo passo mentre il secondo fallisce, i suoi clienti sarebbero comprensibilmente sconvolti. È consigliabile usare questa esercitazione e implementare i miglioramenti apportati a DAL per supportare le transazioni di database anche se non si prevede di usarle nel batch di inserimento, aggiornamento ed eliminazione delle interfacce che verranno eseguite nelle tre esercitazioni seguenti.
Panoramica delle transazioni
La maggior parte dei database include il supporto per le transazioni, che consentono di raggruppare più comandi di database in una singola unità logica di lavoro. I comandi di database che costituiscono una transazione sono garantiti come atomici, ovvero tutti i comandi avranno esito negativo o tutti avranno esito positivo.
In generale, le transazioni vengono implementate tramite istruzioni SQL usando il modello seguente:
- Indicare l'inizio di una transazione.
- Eseguire le istruzioni SQL che costituiscono la transazione.
- Se si verifica un errore in una delle istruzioni del passaggio 2, eseguire il rollback della transazione.
- Se tutte le istruzioni del passaggio 2 vengono completate senza errori, eseguire il commit della transazione.
Le istruzioni SQL utilizzate per creare, eseguire il commit e annullare la transazione possono essere immesse manualmente durante la scrittura di script SQL o la creazione di stored procedure, oppure tramite mezzi programmatici usando ADO.NET o le classi nello spazio dei nomi
Annotazioni
La TransactionScope classe nello System.Transactions spazio dei nomi consente agli sviluppatori di eseguire il wrapping a livello di codice di una serie di istruzioni nell'ambito di una transazione e include il supporto per transazioni complesse che coinvolgono più origini, ad esempio due database diversi o anche tipi eterogenei di archivi dati, ad esempio un database Microsoft SQL Server, un database Oracle e un servizio Web. Ho deciso di usare le transazioni ADO.NET per questa esercitazione invece della TransactionScope classe perché ADO.NET è più specifico per le transazioni di database e, in molti casi, è molto meno intensivo delle risorse. Inoltre, in determinati scenari la TransactionScope classe usa Microsoft Distributed Transaction Coordinator (MSDTC). I problemi di configurazione, implementazione e prestazioni relativi a MSDTC lo rendono un argomento piuttosto specializzato e avanzato e oltre l'ambito di queste esercitazioni.
Quando si lavora con il provider SqlClient in ADO.NET, le transazioni vengono avviate tramite una chiamata al metodo della classe SqlConnection che restituisce un oggetto SqlTransaction. Le istruzioni di modifica dei dati che costituiscono la transazione vengono inserite all'interno di un try...catch blocco. Se si verifica un errore in un'istruzione nel try blocco, l'esecuzione viene trasferita al catch blocco in cui è possibile fare il rollback della transazione tramite il metodo SqlTransactionRollback. Se tutte le istruzioni vengono completate correttamente, una chiamata al SqlTransaction oggetto tramite il metodo Commit alla fine del blocco try conferma la transazione. Il frammento di codice seguente illustra questo modello.
' Create the SqlTransaction object
Dim myTransaction As SqlTransaction = SqlConnectionObject.BeginTransaction();
Try
'
' ... Perform the database transaction�s data modification statements...
'
' If we reach here, no errors, so commit the transaction
myTransaction.Commit()
Catch
' If we reach here, there was an error, so rollback the transaction
myTransaction.Rollback()
Throw
End Try
Per impostazione predefinita, gli oggetti TableAdapter in un dataset tipizzato non usano transazioni. Per fornire supporto per le transazioni, è necessario aumentare le classi TableAdapter per includere metodi aggiuntivi che usano il modello precedente per eseguire una serie di istruzioni di modifica dei dati nell'ambito di una transazione. Nel passaggio 2 verrà illustrato come usare classi parziali per aggiungere questi metodi.
Passaggio 1: Creare le pagine Web per lavorare con i dati in batch
Prima di iniziare a esplorare come aumentare il dal per supportare le transazioni di database, è necessario prima di tutto creare le pagine Web ASP.NET necessarie per questa esercitazione e le tre seguenti. Per iniziare, aggiungere una nuova cartella denominata BatchData e quindi aggiungere le pagine di ASP.NET seguenti, associando ogni pagina alla Site.master pagina master.
Default.aspxTransactions.aspxBatchUpdate.aspxBatchDelete.aspxBatchInsert.aspx
Figura 1: Aggiungere le pagine ASP.NET per le esercitazioni SqlDataSource-Related
Come per le altre cartelle, Default.aspx userà il SectionLevelTutorialListing.ascx controllo utente per elencare le esercitazioni all'interno della relativa sezione. Aggiungere quindi questo controllo utente a Default.aspx trascinandolo da Esplora soluzioni nella visualizzazione di progettazione della pagina.
Figura 2: Aggiungere il SectionLevelTutorialListing.ascx controllo utente a Default.aspx (fare clic per visualizzare l'immagine a dimensione intera)
Infine, aggiungi queste quattro pagine come voci nel file Web.sitemap. In particolare, aggiungere il markup seguente dopo la personalizzazione della mappa <siteMapNode>del sito :
<siteMapNode title="Working with Batched Data"
url="~/BatchData/Default.aspx"
description="Learn how to perform batch operations as opposed to
per-row operations.">
<siteMapNode title="Adding Support for Transactions"
url="~/BatchData/Transactions.aspx"
description="See how to extend the Data Access Layer to support
database transactions." />
<siteMapNode title="Batch Updating"
url="~/BatchData/BatchUpdate.aspx"
description="Build a batch updating interface, where each row in a
GridView is editable." />
<siteMapNode title="Batch Deleting"
url="~/BatchData/BatchDelete.aspx"
description="Explore how to create an interface for batch deleting
by adding a CheckBox to each GridView row." />
<siteMapNode title="Batch Inserting"
url="~/BatchData/BatchInsert.aspx"
description="Examine the steps needed to create a batch inserting
interface, where multiple records can be created at the
click of a button." />
</siteMapNode>
Dopo l'aggiornamento Web.sitemap, dedica qualche minuto per visitare il sito web delle esercitazioni tramite un browser. Il menu a sinistra ora include elementi per i tutorial su come lavorare con dati in batch.
La mappa del sito ora include le voci per le esercitazioni su come utilizzare i dati in batch
Figura 3: La mappa del sito ora comprende le voci relative all'utilizzo di tutorial sui dati elaborati in batch.
Passaggio 2: Aggiornamento del livello di accesso ai dati per supportare le transazioni di database
Come illustrato di nuovo nella prima esercitazione, Creazione di un livello di accesso ai dati, il set di dati tipizzato in DAL è costituito da DataTables e TableAdapter. Le tabelle DataTable contengono dati mentre gli oggetti TableAdapter forniscono la funzionalità per leggere i dati dal database nelle tabelle DataTable, aggiornare il database con le modifiche apportate alle tabelle DataTable e così via. Tenere presente che i TableAdapter forniscono due modelli per l'aggiornamento dei dati, denominati Aggiornamento batch e DB-Direct. Con il modello di aggiornamento batch, l'oggetto TableAdapter viene passato a un oggetto DataSet, DataTable o a una raccolta di DataRows. I dati vengono enumerati e per ogni riga inserita, modificata o eliminata, InsertCommand, UpdateCommand o DeleteCommand vengono eseguiti. Con il modello DB-Direct, il TableAdapter viene invece passato i valori delle colonne necessarie per l'inserimento, l'aggiornamento o l'eliminazione di un singolo record. Il metodo DB Direct pattern usa quindi i valori passati per eseguire l'istruzione appropriata InsertCommand, UpdateCommand, o DeleteCommand.
Indipendentemente dal modello di aggiornamento usato, i metodi generati automaticamente da TableAdapters non usano transazioni. Per impostazione predefinita, ogni operazione di inserimento, aggiornamento o eliminazione eseguita da TableAdapter viene considerata come una singola operazione discreta. Si supponga, ad esempio, che il modello DB-Direct venga usato da parte di codice nel BLL per inserire dieci record nel database. Questo codice chiamerebbe il metodo TableAdapter Insert dieci volte. Se i primi cinque inserimenti hanno esito positivo, ma il sesto ha generato un'eccezione, i primi cinque record inseriti rimarranno nel database. Analogamente, se il modello di aggiornamento batch viene usato per eseguire inserimenti, aggiornamenti ed eliminazioni nelle righe inserite, modificate ed eliminate in una DataTable, se le prime modifiche sono riuscite, ma in seguito si è verificato un errore, le modifiche precedenti completate rimarranno nel database.
In alcuni scenari si vuole garantire l'atomicità in una serie di modifiche. A tale scopo, è necessario estendere manualmente TableAdapter aggiungendo nuovi metodi che eseguono InsertCommand, UpdateCommande DeleteCommand sotto l'ambito di una transazione. In Creazione di un livello di accesso ai dati è stato esaminato l'uso di classi parziali per estendere la funzionalità delle tabelle DataTable all'interno del dataset tipizzato. Questa tecnica può essere usata anche con TableAdapters.
Il set di dati Northwind.xsd tipizzato si trova nella App_Code sottocartella della DAL cartella. Creare una sottocartella nella DAL cartella denominata TransactionSupport e aggiungere un nuovo file di classe denominato ProductsTableAdapter.TransactionSupport.vb (vedere la figura 4). Questo file conterrà l'implementazione parziale di ProductsTableAdapter che include metodi per eseguire modifiche ai dati tramite una transazione.
Figura 4: Aggiungere una cartella denominata TransactionSupport e un file di classe denominato ProductsTableAdapter.TransactionSupport.vb
Immettere il codice seguente nel ProductsTableAdapter.TransactionSupport.vb file:
Imports System.Data
Imports System.Data.SqlClient
Namespace NorthwindTableAdapters
Partial Public Class ProductsTableAdapter
Private _transaction As SqlTransaction
Private Property Transaction() As SqlTransaction
Get
Return Me._transaction
End Get
Set(ByVal Value As SqlTransaction)
Me._transaction = Value
End Set
End Property
Public Sub BeginTransaction()
' Open the connection, if needed
If Me.Connection.State <> ConnectionState.Open Then
Me.Connection.Open()
End If
' Create the transaction and assign it to the Transaction property
Me.Transaction = Me.Connection.BeginTransaction()
' Attach the transaction to the Adapters
For Each command As SqlCommand In Me.CommandCollection
command.Transaction = Me.Transaction
Next
Me.Adapter.InsertCommand.Transaction = Me.Transaction
Me.Adapter.UpdateCommand.Transaction = Me.Transaction
Me.Adapter.DeleteCommand.Transaction = Me.Transaction
End Sub
Public Sub CommitTransaction()
' Commit the transaction
Me.Transaction.Commit()
' Close the connection
Me.Connection.Close()
End Sub
Public Sub RollbackTransaction()
' Rollback the transaction
Me.Transaction.Rollback()
' Close the connection
Me.Connection.Close()
End Sub
End Class
End Namespace
Nella dichiarazione di classe, la parola chiave Partial indica al compilatore che i membri definiti all'interno devono essere aggiunti alla classe ProductsTableAdapter nel namespace NorthwindTableAdapters. Si noti l'istruzione Imports System.Data.SqlClient all'inizio del file. Poiché TableAdapter è stato configurato per l'uso del provider SqlClient, usa internamente un SqlDataAdapter oggetto per eseguire i comandi al database. Di conseguenza, è necessario usare la SqlTransaction classe per avviare la transazione e quindi eseguirne il commit o il rollback. Se si usa un archivio dati diverso da Microsoft SQL Server, sarà necessario usare il provider appropriato.
Questi metodi forniscono i blocchi predefiniti necessari per avviare, eseguire il rollback ed eseguire il commit di una transazione. Sono contrassegnati come Public, il che consente di utilizzarli dall'interno di ProductsTableAdapter, da un'altra classe nel DAL o da un altro livello nell'architettura, ad esempio la BLL.
BeginTransaction apre il SqlConnection interno di TableAdapter (se necessario), avvia la transazione e la assegna alla proprietà Transaction, collegando poi la transazione agli oggetti interni SqlDataAdapter di SqlCommand.
CommitTransaction e RollbackTransaction richiamano rispettivamente i metodi Commit e Rollback dell'oggetto Transaction prima di chiudere l'oggetto interno Connection.
Passaggio 3: Aggiunta di metodi per aggiornare ed eliminare dati sotto l'egida di una transazione
Dopo aver completato questi metodi, è possibile aggiungere metodi a ProductsDataTable o BLL che eseguono una serie di comandi sotto l'ambito di una transazione. Il metodo seguente usa il modello di aggiornamento batch per aggiornare un'istanza ProductsDataTable usando una transazione. Avvia una transazione chiamando il BeginTransaction metodo e quindi usa un Try...Catch blocco per rilasciare le istruzioni di modifica dei dati. Se la chiamata al metodo dell'oggetto AdapterUpdate provoca un'eccezione, l'esecuzione passerà al blocco catch dove verrà eseguito il rollback della transazione e l'eccezione verrà rilanciata. Tenere presente che Update implementa il metodo schema di aggiornamento batch enumerando le righe del ProductsDataTable fornito ed eseguendo le operazioni necessarie InsertCommand, UpdateCommand e DeleteCommand s. Se uno di questi comandi genera un errore, viene eseguito il rollback della transazione annullando le modifiche precedenti apportate durante la durata della transazione. Se l'istruzione Update viene completata senza errori, viene eseguito l'intero commit della transazione.
Public Function UpdateWithTransaction _
(ByVal dataTable As Northwind.ProductsDataTable) As Integer
Me.BeginTransaction()
Try
' Perform the update on the DataTable
Dim returnValue As Integer = Me.Adapter.Update(dataTable)
' If we reach here, no errors, so commit the transaction
Me.CommitTransaction()
Return returnValue
Catch
' If we reach here, there was an error, so rollback the transaction
Me.RollbackTransaction()
Throw
End Try
End Function
Aggiungere il UpdateWithTransaction metodo alla ProductsTableAdapter classe tramite la classe parziale in ProductsTableAdapter.TransactionSupport.vb. In alternativa, questo metodo può essere aggiunto alla classe del Business Logic Layer ProductsBLL con alcune piccole modifiche sintattiche. Vale a dire, la parola Me chiave in Me.BeginTransaction(), Me.CommitTransaction(), e Me.RollbackTransaction() dovrebbe essere sostituita con Adapter (ricordiamo che Adapter è il nome di una proprietà in ProductsBLL di tipo ProductsTableAdapter).
Il UpdateWithTransaction metodo usa il modello di aggiornamento batch, ma è anche possibile usare una serie di chiamate DB-Direct nell'ambito di una transazione, come illustrato nel metodo seguente. Il DeleteProductsWithTransaction metodo accetta come input un List(Of T) di tipo Integer, ovvero gli ProductID oggetti da eliminare. Il metodo avvia la transazione tramite una chiamata a BeginTransaction e quindi, nel Try blocco, scorre l'elenco fornito chiamando il metodo modello Delete DB-Direct per ogni ProductID valore. Se una delle chiamate a Delete ha esito negativo, il controllo viene trasferito al blocco Catch dove la transazione viene annullata e l'eccezione viene rilanciata. Se tutte le chiamate a Delete hanno esito positivo, viene eseguito il commit della transazione. Aggiungere questo metodo alla ProductsBLL classe .
Public Sub DeleteProductsWithTransaction _
(ByVal productIDs As System.Collections.Generic.List(Of Integer))
' Start the transaction
Adapter.BeginTransaction()
Try
' Delete each product specified in the list
For Each productID As Integer In productIDs
Adapter.Delete(productID)
Next
' Commit the transaction
Adapter.CommitTransaction()
Catch
' There was an error - rollback the transaction
Adapter.RollbackTransaction()
Throw
End Try
End Sub
Applicazione di transazioni attraverso più TableAdapter
Il codice correlato alla transazione esaminato in questa esercitazione consente di considerare più istruzioni contro l'oggetto ProductsTableAdapter come operazione atomica. Ma cosa accade se è necessario eseguire atomicamente più modifiche a tabelle di database diverse? Ad esempio, quando si elimina una categoria, potrebbe essere necessario riassegnare i prodotti correnti ad altre categorie. Questi due passaggi riassegnano i prodotti ed eliminano la categoria devono essere eseguiti come operazione atomica.
ProductsTableAdapter Include tuttavia solo i metodi per la modifica della Products tabella e include CategoriesTableAdapter solo i metodi per la modifica della Categories tabella. In che modo una transazione può includere entrambi gli oggetti TableAdapter?
Un'opzione consiste nell'aggiungere un metodo al CategoriesTableAdapter denominato DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) e fare in modo che tale metodo chiami una stored procedure che riassegna i prodotti ed elimina la categoria all'interno dell'ambito di una transazione definita all'interno della stored procedure. Verrà illustrato come iniziare, eseguire il commit e il rollback delle transazioni nelle stored procedure in un'esercitazione futura.
Un'altra opzione consiste nel creare una classe helper nel DAL che contiene il metodo DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID). Questo metodo creerebbe un'istanza di CategoriesTableAdapter e ProductsTableAdapter, quindi imposterebbe le proprietà di questi due TableAdapter Connection sulla stessa istanza SqlConnection. A questo punto, uno dei due TableAdapter avvia la transazione con una chiamata a BeginTransaction. I metodi di TableAdapters per riassegnare i prodotti ed eliminare la categoria vengono richiamati all'interno di un blocco Try...Catch, con la transazione che viene eseguita o annullata secondo necessità.
Passaggio 4: Aggiunta delUpdateWithTransactionmetodo al livello della logica di business
Nel passaggio 3 è stato aggiunto un UpdateWithTransaction metodo a ProductsTableAdapter in DAL. È necessario aggiungere un metodo corrispondente al BLL. Anche se il Livello di presentazione potrebbe chiamare direttamente il DAL per richiamare il metodo UpdateWithTransaction, queste esercitazioni hanno cercato di definire un'architettura stratificata che isola il DAL dal Livello di presentazione. Pertanto, è opportuno continuare con questo approccio.
Aprire il file di ProductsBLL classe e aggiungere un metodo denominato UpdateWithTransaction che semplicemente chiama il metodo DAL corrispondente. A questo punto dovrebbero essere presenti due nuovi metodi in ProductsBLL: UpdateWithTransaction, che è stato appena aggiunto e DeleteProductsWithTransaction, che è stato aggiunto nel passaggio 3.
Public Function UpdateWithTransaction _
(ByVal products As Northwind.ProductsDataTable) As Integer
Return Adapter.UpdateWithTransaction(products)
End Function
Public Sub DeleteProductsWithTransaction _
(ByVal productIDs As System.Collections.Generic.List(Of Integer))
' Start the transaction
Adapter.BeginTransaction()
Try
' Delete each product specified in the list
For Each productID As Integer In productIDs
Adapter.Delete(productID)
Next
' Commit the transaction
Adapter.CommitTransaction()
Catch
' There was an error - rollback the transaction
Adapter.RollbackTransaction()
Throw
End Try
End Sub
Annotazioni
Questi metodi non includono l'attributo DataObjectMethodAttribute assegnato alla maggior parte degli altri metodi nella ProductsBLL classe perché richiameremo questi metodi direttamente dalle classi code-behind delle pagine di ASP.NET. Tenere presente che DataObjectMethodAttribute viene usato per contrassegnare i metodi da visualizzare nella procedura guidata Configura origine dati di ObjectDataSource e in quale scheda (SELECT, UPDATE, INSERT o DELETE). Poiché GridView non dispone di qualsiasi supporto predefinito per la modifica o l'eliminazione in batch, è necessario richiamare questi metodi a livello di codice anziché usare l'approccio dichiarativo senza codice.
Passaggio 5: Aggiornamento atomico dei dati del database dal livello di presentazione
Per illustrare l'effetto che la transazione ha quando si aggiorna un batch di record, è possibile creare un'interfaccia utente che elenca tutti i prodotti in gridView e include un controllo Web Button che, quando si fa clic, riassegna i valori dei prodotti CategoryID . In particolare, la riassegnazione della categoria procederà in modo che ai primi prodotti venga assegnato un valore valido CategoryID mentre altri vengono assegnati intenzionalmente un valore inesistente CategoryID . Se si tenta di aggiornare il database con un prodotto il cui CategoryID non corrisponde a una categoria esistente, CategoryIDsi verificherà una violazione del vincolo di chiave esterna e verrà generata un'eccezione. Ciò che vedremo in questo esempio è che quando si usa una transazione, l'eccezione generata dalla violazione del vincolo di chiave esterna causerà il rollback delle modifiche valide CategoryID precedenti. Quando non si usa una transazione, tuttavia, le modifiche apportate alle categorie iniziali rimarranno.
Per iniziare, aprire la Transactions.aspx pagina nella BatchData cartella e trascinare GridView dalla Toolbox sul Designer. Impostare il suo ID su Products e, dal suo smart tag, collegarlo a un nuovo ObjectDataSource denominato ProductsDataSource. Configurare ObjectDataSource per recuperare i dati dal metodo della classe ProductsBLLGetProducts. Si tratta di un GridView di sola lettura, quindi impostare gli elenchi a discesa nelle schede UPDATE, INSERT e DELETE su (Nessuno) e cliccare su Fine.
Figura 5: Configurare ObjectDataSource per l'uso del metodo della classe ProductsBLLGetProducts (fare clic per visualizzare l'immagine a dimensione intera)
Figura 6: Impostare gli elenchi a discesa nelle schede UPDATE, INSERT e DELETE su (Nessuno) (Fare clic per visualizzare l'immagine a dimensione intera)
Dopo aver completato la procedura guidata Configura origine dati, Visual Studio creerà BoundFields e checkBoxField per i campi dati del prodotto. Rimuovere tutti questi campi ad eccezione di ProductID, ProductName, CategoryID e CategoryName e rinominare rispettivamente le proprietà ProductName e CategoryName BoundFields HeaderText in Product e Category. Dallo smart tag selezionare l'opzione Abilita paging. Dopo aver apportato queste modifiche, il markup dichiarativo di GridView e ObjectDataSource dovrebbe essere simile al seguente:
<asp:GridView ID="Products" runat="server" AllowPaging="True"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductID" HeaderText="ProductID"
InsertVisible="False" ReadOnly="True"
SortExpression="ProductID" />
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
SortExpression="CategoryID" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
SortExpression="CategoryName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
Successivamente, aggiungere tre controlli Web Pulsante sopra il GridView. Impostare la proprietà Testo della prima opzione pulsante su Aggiorna griglia, la seconda su Modifica categorie (CON TRANSAZIONE) e la terza su Modifica categorie (SENZA TRANSAZIONE).
<p>
<asp:Button ID="RefreshGrid" runat="server" Text="Refresh Grid" />
</p>
<p>
<asp:Button ID="ModifyCategoriesWithTransaction" runat="server"
Text="Modify Categories (WITH TRANSACTION)" />
</p>
<p>
<asp:Button ID="ModifyCategoriesWithoutTransaction" runat="server"
Text="Modify Categories (WITHOUT TRANSACTION)" />
</p>
A questo punto la visualizzazione Progettazione in Visual Studio dovrebbe essere simile alla schermata illustrata nella figura 7.
Figura 7: La Pagina contiene un controllo GridView e tre controlli Web di pulsante (fare clic per visualizzare l'immagine a grandezza naturale)
Creare gestori eventi per ognuno dei tre eventi del Button Click e usare il codice seguente:
Protected Sub RefreshGrid_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles RefreshGrid.Click
Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithTransaction_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles ModifyCategoriesWithTransaction.Click
' Get the set of products
Dim productsAPI As New ProductsBLL()
Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
' Update each product's CategoryID
For Each product As Northwind.ProductsRow In productsData
product.CategoryID = product.ProductID
Next
' Update the data using a transaction
productsAPI.UpdateWithTransaction(productsData)
' Refresh the Grid
Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithoutTransaction_Click _
(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles ModifyCategoriesWithoutTransaction.Click
' Get the set of products
Dim productsAPI As New ProductsBLL()
Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
' Update each product's CategoryID
For Each product As Northwind.ProductsRow In productsData
product.CategoryID = product.ProductID
Next
' Update the data WITHOUT using a transaction
Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
productsAdapter.Update(productsData)
' Refresh the Grid
Products.DataBind()
End Sub
Il gestore eventi del pulsante di Click aggiornamento ribelega semplicemente i dati al GridView chiamando il metodo Products GridView.DataBind
Il secondo gestore eventi riassegna i prodotti CategoryID e usa il nuovo metodo di transazione da BLL per eseguire gli aggiornamenti del database sotto l'ambito di una transazione. Si noti che il valore di CategoryID di ciascun prodotto è impostato arbitrariamente sullo stesso valore di ProductID. Questo funzionerà bene per i primi alcuni prodotti, poiché quei prodotti hanno valori ProductID che si mappano casualmente a CategoryID validi. Ma quando gli elementi ProductID diventano troppo grandi, questa sovrapposizione coincidente di ProductID e CategoryID non si applica più.
Il terzo Click gestore eventi aggiorna i prodotti CategoryID nello stesso modo, ma invia l'aggiornamento al database usando il metodo predefinito ProductsTableAdapterUpdate. Questo Update metodo non avvolge la serie di comandi all'interno di una transazione, quindi le modifiche effettuate prima dell'insorgere del primo errore di violazione del vincolo di chiave esterna continueranno a persistere.
Per illustrare questo comportamento, visitare questa pagina tramite un browser. Inizialmente dovrebbe essere visualizzata la prima pagina di dati, come illustrato nella figura 8. Fare quindi clic sul pulsante Modifica categorie (CON TRANSAZIONE). In questo modo si verificherà un postback e si tenterà di aggiornare tutti i valori dei prodotti CategoryID , ma si verificherà una violazione del vincolo di chiave esterna (vedere la figura 9).
Figura 8: I prodotti vengono visualizzati in un GridView a paginazione (fare clic per visualizzare l'immagine a schermo intero).
Figura 9: Riassegnazione delle categorie Comporta una violazione del vincolo di chiave esterna (fare clic per visualizzare l'immagine a dimensione intera)
Fare clic sul pulsante Indietro del browser e quindi sul pulsante Aggiorna griglia. Dopo l'aggiornamento dei dati dovrebbe essere visualizzato esattamente lo stesso output illustrato nella figura 8. Nonostante alcuni prodotti CategoryID siano stati modificati per valori legali e aggiornati nel database, sono stati annullati quando si è verificata la violazione del vincolo di chiave esterna.
Ora prova a fare clic sul pulsante Modifica categorie (SENZA TRANSAZIONE). Ciò comporterà lo stesso errore di violazione del vincolo di chiave esterna (vedere la figura 9), ma questa volta i prodotti i cui CategoryID valori sono stati modificati in un valore legale non verranno ripristinati. Premere il pulsante Indietro del browser e quindi il pulsante Aggiorna griglia. Come illustrato nella figura 10, i CategoryID dei primi otto prodotti sono stati riassegnati. Nella figura 8, ad esempio, Chang aveva un CategoryID valore pari a 1, ma nella figura 10 è stato riassegnato a 2.
Figura 10: Alcuni valori prodotti CategoryID sono stati aggiornati mentre altri non erano (fare clic per visualizzare l'immagine a dimensione intera)
Riassunto
Per impostazione predefinita, i metodi di TableAdapter non eseguono il wrapping delle istruzioni di database eseguite nell'ambito di una transazione, ma con un piccolo lavoro è possibile aggiungere metodi che creeranno, eseguiranno il commit e il rollback di una transazione. In questa esercitazione sono stati creati tre metodi di questo tipo nella ProductsTableAdapter classe , BeginTransactionCommitTransactione RollbackTransaction. È stato illustrato come usare questi metodi insieme a un Try...Catch blocco per rendere atomica una serie di istruzioni di modifica dei dati. In particolare, abbiamo creato il UpdateWithTransaction metodo in ProductsTableAdapter, che usa il modello di aggiornamento batch per eseguire le modifiche necessarie alle righe di un oggetto ProductsDataTable fornito. Abbiamo anche aggiunto il metodo DeleteProductsWithTransaction alla classe ProductsBLL nel BLL, che accetta un List di valori ProductID come input e chiama il metodo DB-Direct pattern Delete per ciascun ProductID. Entrambi i metodi iniziano creando una transazione e quindi eseguendo le istruzioni di modifica dei dati all'interno di un Try...Catch blocco. Se si verifica un'eccezione, viene eseguito il rollback della transazione, in caso contrario viene eseguito il commit.
Il passaggio 5 ha illustrato l'effetto degli aggiornamenti batch transazionali rispetto agli aggiornamenti batch che non utilizzano una transazione. Nelle tre esercitazioni successive si baseranno sulle basi definite in questa esercitazione e si creeranno interfacce utente per l'esecuzione di aggiornamenti batch, eliminazioni e inserimenti.
Buon programmatori!
Altre informazioni
Per altre informazioni sugli argomenti illustrati in questa esercitazione, vedere le risorse seguenti:
-
Transazioni semplificate:
System.Transactions - TransactionScope e DataAdapter
- Uso delle transazioni di database Oracle in .NET
Informazioni sull'autore
Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, ha lavorato 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 a mitchell@4GuysFromRolla.com.
Grazie speciale a
Questa serie di esercitazioni è stata esaminata da molti revisori competenti. I revisori principali per questa esercitazione erano Dave Gardner, Hilton Giesenow e Teresa Murphy. Si è interessati a esaminare i prossimi articoli MSDN? In tal caso, mandami un messaggio a mitchell@4GuysFromRolla.com.