Condividi tramite


Wrapping delle modifiche al database in una transazione (C#)

di Scott Mitchell

Scarica il PDF

Questa esercitazione è la prima di quattro che esamina l'aggiornamento, l'eliminazione e l'inserimento di batch di dati. In questa esercitazione viene illustrato come le transazioni di database consentono l'esecuzione di modifiche batch come operazione atomica, che garantisce che tutti i passaggi abbiano esito positivo o che tutti i passaggi abbiano esito negativo.

Introduzione

Come abbiamo visto a partire dall'esercitazione Panoramica dell'inserimento, dell'aggiornamento e dell'eliminazione dei 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 su base per riga. Tuttavia, in alcuni scenari, questo è insufficiente 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 relative al 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. Anziché 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 verrà illustrato 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. Prendere in considerazione un'interfaccia di eliminazione batch: cosa dovrebbe verificarsi 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? L'eliminazione del primo record deve essere eseguito il rollback o è accettabile che il primo record rimanga eliminato?

Se si vuole che l'operazione batch venga considerata come un'operazione atomica, una in cui tutti i passaggi hanno esito positivo o tutti i passaggi hanno esito negativo, il livello di accesso ai dati deve essere incrementato per includere il supporto per le transazioni di database. Le transazioni di database garantiscono l'atomicità per il set di INSERTistruzioni , UPDATEe DELETE eseguite sotto l'ambito della transazione e sono una funzionalità supportata dalla maggior parte di tutti i sistemi di database moderni.

In questa esercitazione si esaminerà come estendere il DAL per usare le transazioni di database. Le esercitazioni successive esamineranno l'implementazione di pagine Web per l'inserimento, l'aggiornamento e l'eliminazione di interfacce batch. Iniziamo!

Nota

Quando si modificano i dati in una transazione batch, l'atomicità non è sempre necessaria. In alcuni scenari, potrebbe essere accettabile avere alcune modifiche ai dati riuscite e altre nello stesso batch hanno 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, non è necessario modificare il SERVIZIO di accesso per supportare le transazioni di database. Esistono tuttavia altri scenari di operazione batch, dove l'atomicità è vitale. Quando un cliente sposta i fondi da un conto bancario a un altro, due operazioni devono essere eseguite: i fondi devono essere detratti dal primo conto e quindi aggiunti al secondo. Anche se la banca potrebbe non avere il primo passaggio riuscito, ma il secondo passaggio ha esito negativo, i suoi clienti sarebbero comprensibilmente sconvolti. È consigliabile usare questa esercitazione e implementare i miglioramenti apportati al servizio di distribuzione per supportare le transazioni di database anche se non si prevede di usarli nell'inserimento in batch, l'aggiornamento e l'eliminazione delle interfacce verranno creati 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 del database che comprendono una transazione sono garantiti 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:

  1. Indicare l'inizio di una transazione.
  2. Eseguire le istruzioni SQL che comprendono la transazione.
  3. Se si verifica un errore in una delle istruzioni del passaggio 2, eseguire il rollback della transazione.
  4. Se tutte le istruzioni del passaggio 2 vengono completate senza errori, eseguire il commit della transazione.

Le istruzioni SQL usate per creare, eseguire il commit e il rollback della 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 System.Transactions spazio dei nomi. In questa esercitazione verrà esaminata solo la gestione delle transazioni usando ADO.NET. In un'esercitazione futura verrà illustrato come usare stored procedure nel livello di accesso ai dati, in cui verranno esaminate le istruzioni SQL per la creazione, il rollback e il commit delle transazioni.

Nota

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 anziché la TransactionScope classe perché ADO.NET è più specifica per le transazioni di database e, in molti casi, è molto meno intensivo di risorse. Inoltre, in determinati scenari la TransactionScope classe usa Microsoft Distributed Transaction Coordinator (MSDTC). La configurazione, l'implementazione e i problemi di prestazioni relativi a MSDTC lo rendono un argomento piuttosto specializzato e avanzato e oltre l'ambito di queste esercitazioni.

Quando si usa il provider SqlClient in ADO.NET, le transazioni vengono avviate tramite una chiamata al SqlConnection metodo della BeginTransactionclasse, che restituisce un SqlTransaction oggetto. 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 trasferisce al catch blocco in cui la transazione può essere eseguito il rollback tramite il metodo dell'oggettoRollbackSqlTransaction. Se tutte le istruzioni vengono completate correttamente, una chiamata al SqlTransaction metodo dell'oggetto Commit alla fine del try blocco esegue il commit della transazione. Il frammento di codice seguente illustra questo modello. Vedere Gestione della coerenza del database con le transazioni.

// Create the SqlTransaction object
SqlTransaction myTransaction = 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;
}

Per impostazione predefinita, i TableAdapter in un DataSet tipizzato non usano le 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: Creazione dell'uso delle pagine Web dei dati in batch

Prima di iniziare a esplorare come aumentare il dal per supportare le transazioni di database, è prima necessario creare le pagine Web ASP.NET necessarie per questa esercitazione e le tre che seguono. Iniziare aggiungendo una nuova cartella denominata BatchData e quindi aggiungere le pagine di ASP.NET seguenti, associando ogni pagina alla Site.master pagina master.

  • Default.aspx
  • Transactions.aspx
  • BatchUpdate.aspx
  • BatchDelete.aspx
  • BatchInsert.aspx

Aggiungere le pagine ASP.NET per le esercitazioni SqlDataSource-Related

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 dalla Esplora soluzioni nella visualizzazione Progettazione della pagina.

Aggiungere il controllo utente SectionLevelTutorialListing.ascx a Default.aspx

Figura 2: Aggiungere il controllo utente a Default.aspx (Fare clic per visualizzare l'immagineSectionLevelTutorialListing.ascx full-size)

Infine, aggiungere queste quattro pagine come voci al Web.sitemap file. 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, passare un momento per visualizzare il sito Web delle esercitazioni tramite un browser. Il menu a sinistra include ora elementi per l'uso di esercitazioni sui dati in batch.

La mappa del sito include ora voci per l'uso di esercitazioni sui dati in batch

Figura 3: La mappa del sito include ora voci per l'uso delle esercitazioni sui dati in batch

Passaggio 2: Aggiornamento del livello di accesso ai dati per supportare le transazioni di database

Come illustrato di nuovo nella prima esercitazione, la creazione di un livello di accesso ai dati, l'oggetto DataSet tipizzato in DAL è costituito da DataTables e TableAdapters. DataTables contiene dati mentre TableAdapters fornisce la funzionalità per leggere i dati dal database nelle tabelle dati, per aggiornare il database con le modifiche apportate alle tabelle dati e così via. Tenere presente che tableAdapters fornisce due modelli per l'aggiornamento dei dati, a cui si fa riferimento come Aggiornamento batch e DB-Direct. Con il modello di aggiornamento batch, TableAdapter viene passato a DataSet, DataTable o a una raccolta di DataRows. Questi dati vengono enumerati e per ogni riga inserita, modificata o eliminata, l'oggetto InsertCommand, UpdateCommando DeleteCommand viene eseguito. Con il modello DB-Direct, 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 tali valori passati per eseguire l'istruzione , o DeleteCommand appropriataUpdateCommandInsertCommand.

Indipendentemente dal modello di aggiornamento usato, i metodi generati automaticamente da TableAdapters non usano le transazioni. Per impostazione predefinita, ogni inserimento, aggiornamento o eliminazione eseguito dall'oggetto TableAdapter viene considerato come una singola operazione discreta. Si supponga, ad esempio, che il modello di DB-Direct venga usato da un 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 alle righe inserite, modificate ed eliminate in una tabella dati, se la prima modifica ha avuto esito positivo, ma una successiva ha rilevato un errore, le modifiche precedenti completate rimarranno nel database.

In alcuni scenari si vuole garantire l'atomicità in una serie di modifiche. A questo scopo, è necessario estendere manualmente TableAdapter aggiungendo nuovi metodi che eseguono , InsertCommandUpdateCommande 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 dati all'interno di DataSet tipizzato. Questa tecnica può essere usata anche con TableAdapters.

Il dataset Northwind.xsd tipizzato si trova nella sottocartella della DALApp_Code cartella. Creare una sottocartella nella DAL cartella denominata TransactionSupport e aggiungere un nuovo file di classe denominato ProductsTableAdapter.TransactionSupport.cs (vedere la figura 4). Questo file conterrà l'implementazione parziale dell'oggetto ProductsTableAdapter che include metodi per eseguire modifiche ai dati usando una transazione.

Aggiungere una cartella denominata TransactionSupport e un file di classe denominato ProductsTableAdapter.TransactionSupport.cs

Figura 4: Aggiungere una cartella denominata TransactionSupport e un file di classe denominato ProductsTableAdapter.TransactionSupport.cs

Immettere il codice seguente nel ProductsTableAdapter.TransactionSupport.cs file:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace NorthwindTableAdapters
{
    public partial class ProductsTableAdapter
    {
        private SqlTransaction _transaction;
        private SqlTransaction Transaction
        {
            get
            {                
                return this._transaction;
            }
            set
            {
                this._transaction = value;
            }
        }
        public void BeginTransaction()
        {
            // Open the connection, if needed
            if (this.Connection.State != ConnectionState.Open)
                this.Connection.Open();
            // Create the transaction and assign it to the Transaction property
            this.Transaction = this.Connection.BeginTransaction();
            // Attach the transaction to the Adapters
            foreach (SqlCommand command in this.CommandCollection)
            {
                command.Transaction = this.Transaction;
            }
            this.Adapter.InsertCommand.Transaction = this.Transaction;
            this.Adapter.UpdateCommand.Transaction = this.Transaction;
            this.Adapter.DeleteCommand.Transaction = this.Transaction;
        }
        public void CommitTransaction()
        {
            // Commit the transaction
            this.Transaction.Commit();
            // Close the connection
            this.Connection.Close();
        }
        public void RollbackTransaction()
        {
            // Rollback the transaction
            this.Transaction.Rollback();
            // Close the connection
            this.Connection.Close();
        }
   }
}

La partial parola chiave nella dichiarazione di classe qui indica al compilatore che i membri aggiunti all'interno devono essere aggiunti alla ProductsTableAdapter classe nello NorthwindTableAdapters spazio dei nomi. Prendere nota dell'istruzione using System.Data.SqlClient nella parte superiore del file. Poiché TableAdapter è stato configurato per l'uso del provider SqlClient, viene usato internamente un SqlDataAdapter oggetto per eseguire i comandi al database. Di conseguenza, è necessario usare la classe per avviare la transazione e quindi eseguirne il SqlTransaction commit o eseguirne il rollback. Se si usa un archivio dati diverso da Microsoft SQL Server, è necessario usare il provider appropriato.

Questi metodi forniscono i blocchi predefiniti necessari per avviare, eseguire il rollback e eseguire il commit di una transazione. Sono contrassegnati public, consentendo loro di essere usati dall'interno ProductsTableAdapterdi , da un'altra classe nel DAL o da un altro livello nell'architettura, ad esempio il BLL. BeginTransactionapre l'oggetto TableAdapter interno SqlConnection (se necessario), avvia la transazione e lo assegna alla proprietà e associa la transazione agli Transaction oggetti s SqlCommand interniSqlDataAdapter. CommitTransactione RollbackTransaction chiamare rispettivamente l'oggetto s Commit e Rollback i Transaction metodi prima di chiudere l'oggetto internoConnection.

Passaggio 3: Aggiunta di metodi per aggiornare ed eliminare i dati sotto l'ombrello di una transazione

Con 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 Adapter metodo dell'oggetto Update genera un'eccezione, l'esecuzione verrà trasferita al catch blocco in cui verrà eseguito il rollback della transazione e viene generata nuovamente l'eccezione. Tenere presente che il metodo implementa il Update modello di aggiornamento batch enumerando le righe dell'oggetto fornito ProductsDataTable ed eseguendo le operazioni necessarie InsertCommand, UpdateCommande DeleteCommand s. Se uno di questi comandi genera un errore, la transazione viene eseguito il rollback, annullando le modifiche precedenti apportate durante la durata della transazione. Se l'istruzione viene completata senza errori, la Update transazione viene eseguita con il commit nell'intera proprietà.

public int UpdateWithTransaction(Northwind.ProductsDataTable dataTable)
{
    this.BeginTransaction();
    try
    {
        // Perform the update on the DataTable
        int returnValue = this.Adapter.Update(dataTable);
        // If we reach here, no errors, so commit the transaction
        this.CommitTransaction();
        return returnValue;
    }
    catch
    {
        // If we reach here, there was an error, so rollback the transaction
        this.RollbackTransaction();
        throw;
    }
}

Aggiungere il UpdateWithTransaction metodo alla classe tramite la ProductsTableAdapter classe parziale in ProductsTableAdapter.TransactionSupport.cs. In alternativa, questo metodo può essere aggiunto alla classe livello ProductsBLL di logica di business con alcune modifiche sintattiche secondarie. In nome, la parola chiave in this.BeginTransaction(), this.CommitTransaction()e this.RollbackTransaction() deve essere sostituita con Adapter (ricordare che Adapter è il nome di una proprietà in ProductsBLL di tipo ProductsTableAdapter).

Il UpdateWithTransaction metodo usa il modello di aggiornamento batch, ma una serie di chiamate DB-Direct può essere usata anche nell'ambito di una transazione, come illustrato nel metodo seguente. Il DeleteProductsWithTransaction metodo accetta come input un List<T> di tipo int, che sono gli ProductID s da eliminare. Il metodo avvia la transazione tramite una chiamata a BeginTransaction e quindi, nel try blocco, esegue l'iterazione tramite l'elenco fornito chiamando il metodo modello di DB-Direct Delete per ogni ProductID valore. Se una delle chiamate a Delete ha esito negativo, il controllo viene trasferito al catch blocco in cui viene eseguito il rollback della transazione e viene generata nuovamente l'eccezione. Se tutte le chiamate hanno Delete esito positivo, viene eseguito il commit della transazione. Aggiungere questo metodo alla ProductsBLL classe.

public void DeleteProductsWithTransaction
    (System.Collections.Generic.List<int> productIDs)
{
    // Start the transaction
    Adapter.BeginTransaction();
    try
    {
        // Delete each product specified in the list
        foreach (int productID in productIDs)
        {
            Adapter.Delete(productID);
        }
        // Commit the transaction
        Adapter.CommitTransaction();
    }
    catch
    {
        // There was an error - rollback the transaction
        Adapter.RollbackTransaction();
        throw;
    }
}

Applicazione delle transazioni tra più tableAdapter

Il codice correlato alla transazione esaminato in questa esercitazione consente di considerare più istruzioni rispetto all'oggetto ProductsTableAdapter da considerare come operazione atomica. Ma cosa accade se devono essere eseguite atomicamente più modifiche a tabelle di database diverse? Ad esempio, quando si elimina una categoria, potrebbe essere necessario riassegnare i prodotti correnti a un'altra categoria. Questi due passaggi riassegnano i prodotti ed eliminare la categoria devono essere eseguiti come operazione atomica. ProductsTableAdapter Ma include solo metodi per la modifica della Products tabella e CategoriesTableAdapter include solo i metodi per la modifica della Categories tabella. In che modo una transazione può includere sia TableAdapters?

Un'opzione consiste nell'aggiungere un metodo al CategoriesTableAdapter metodo denominato DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) e chiamare tale stored procedure che riassegna i prodotti ed elimina la categoria nell'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 DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) metodo . Questo metodo crea un'istanza CategoriesTableAdapterProductsTableAdapter di e quindi imposta queste due proprietà TableAdapters Connection sulla stessa SqlConnection istanza. A quel punto, uno dei due TableAdapter avvia la transazione con una chiamata a BeginTransaction. I metodi TableAdapters per riassegnare i prodotti ed eliminare la categoria verranno richiamati in un try...catch blocco con il commit della transazione o il rollback in base alle esigenze.

Passaggio 4: Aggiunta delUpdateWithTransactionmetodo al livello di logica di business

Nel passaggio 3 è stato aggiunto un UpdateWithTransaction metodo all'oggetto ProductsTableAdapter in DAL. È necessario aggiungere un metodo corrispondente al BLL. Sebbene il livello di presentazione possa chiamare direttamente verso il basso il dal per richiamare il UpdateWithTransaction metodo, queste esercitazioni hanno cercato di definire un'architettura a livelli che isola il dal livello di presentazione. Pertanto, ci fa continuare questo approccio.

Aprire il file di ProductsBLL classe e aggiungere un metodo denominato UpdateWithTransaction che chiama semplicemente il metodo DAL corrispondente. È ora necessario aggiungere due nuovi metodi in ProductsBLL: UpdateWithTransaction, che è stato appena aggiunto e DeleteProductsWithTransaction, che è stato aggiunto nel passaggio 3.

public int UpdateWithTransaction(Northwind.ProductsDataTable products)
{
    return Adapter.UpdateWithTransaction(products);
}
public void DeleteProductsWithTransaction
    (System.Collections.Generic.List<int> productIDs)
{
    // Start the transaction
    Adapter.BeginTransaction();
    try
    {
        // Delete each product specified in the list
        foreach (int productID in productIDs)
            Adapter.Delete(productID);
        // Commit the transaction
        Adapter.CommitTransaction();
    }
    catch
    {
        // There was an error - rollback the transaction
        Adapter.RollbackTransaction();
        throw;
    }
}

Nota

Questi metodi non includono l'attributo DataObjectMethodAttribute assegnato alla maggior parte degli altri metodi della ProductsBLL classe perché si richiamano questi metodi direttamente dalle classi code-behind delle pagine di ASP.NET. Tenere presente che DataObjectMethodAttribute viene usato per contrassegnare i metodi che devono essere visualizzati nella procedura guidata Configura origine dati di ObjectDataSource e in quale scheda (SELECT, UPDATE, INSERT o DELETE). Poiché GridView non supporta alcun supporto predefinito per la modifica o l'eliminazione in batch, è necessario richiamare questi metodi a livello di codice anziché usare l'approccio dichiarativo 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, consente di creare un'interfaccia utente che elenca tutti i prodotti in GridView e include un controllo Web Button che, quando viene fatto clic, riassegna i valori dei prodotti CategoryID . In particolare, la riassegnazione della categoria procederà in modo che i primi prodotti vengano assegnati un valore valido CategoryID mentre altri vengono assegnati in modo intenzionale a un valore non esistente CategoryID . Se si tenta di aggiornare il database con un prodotto che CategoryID non corrisponde a una categoria esistente, CategoryIDsi verificherà una violazione del vincolo di chiave esterna e verrà generata un'eccezione. Ciò che verrà visualizzato 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 alle categorie iniziali rimarranno.

Iniziare aprendo la Transactions.aspx pagina nella BatchData cartella e trascinando gridView dalla casella degli strumenti nella Designer. ID Impostare su Products e, dal relativo smart tag, associarlo a un nuovo OggettoDataSource denominato ProductsDataSource. Configurare ObjectDataSource per eseguire il pull dei dati dal ProductsBLL metodo della GetProducts classe. Si tratta di un controllo GridView di sola lettura, quindi impostare gli elenchi a discesa nelle schede UPDATE, INSERT e DELETE su (Nessuno) e fare clic su Fine.

Figura 5: Configurare ObjectDataSource per usare il metodo GetProducts della classe ProductsBLL

Figura 5: Figura 5: Configurare ObjectDataSource per usare il metodo class s GetProducts (Fare clic per visualizzare l'immagineProductsBLL full-size)

Impostare la Drop-Down Elenchi nelle schede UPDATE, INSERT e DELETE su (Nessuno)

Figura 6: Impostare la Drop-Down Elenchi nelle schede UPDATE, INSERT e DELETE su (Nessuna) (Fare clic per visualizzare l'immagine a dimensioni complete)

Al termine della procedura guidata Configura origine dati, Visual Studio creerà BoundFields e checkBoxField per i campi dati del prodotto. Rimuovere tutti questi campi, ad eccezione ProductIDdi , ProductName, CategoryIDe CategoryName rinominare rispettivamente le ProductName proprietà e BoundFields HeaderText in Product e CategoryName Category. Nella smart tag selezionare l'opzione Abilita paging. Dopo aver apportato queste modifiche, il markup dichiarativo gridView e ObjectDataSource deve 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>

Aggiungere quindi tre controlli Web Button sopra GridView. Impostare la prima proprietà Text del pulsante su Aggiorna griglia, la seconda su Modifica categorie (WITH TRANSACTION) 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 visualizzata nella figura 7.

La pagina contiene un controllo Web GridView e tre pulsanti

Figura 7: La pagina contiene un controllo Web GridView e tre controlli Web pulsante (fare clic per visualizzare l'immagine full-size)

Creare gestori eventi per ognuno degli eventi di Click tre pulsanti e usare il codice seguente:

protected void RefreshGrid_Click(object sender, EventArgs e)
{
    Products.DataBind();
}
protected void ModifyCategoriesWithTransaction_Click(object sender, EventArgs e)
{
    // Get the set of products
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    // Update each product's CategoryID
    foreach (Northwind.ProductsRow product in products)
    {
        product.CategoryID = product.ProductID;
    }
    // Update the data using a transaction
    productsAPI.UpdateWithTransaction(products);
    // Refresh the Grid
    Products.DataBind();
}
protected void ModifyCategoriesWithoutTransaction_Click(object sender, EventArgs e)
{
    // Get the set of products
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    // Update each product's CategoryID
    foreach (Northwind.ProductsRow product in products)
    {
        product.CategoryID = product.ProductID;
    }
    // Update the data WITHOUT using a transaction
    NorthwindTableAdapters.ProductsTableAdapter productsAdapter = 
        new NorthwindTableAdapters.ProductsTableAdapter();
    productsAdapter.Update(products);
    // Refresh the Grid
    Products.DataBind();
}

Il gestore eventi button di Click aggiornamento esegue semplicemente il ribining dei dati in GridView chiamando il Products metodo 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 ogni prodotto è CategoryID impostato arbitrariamente sullo stesso valore del relativo ProductID. Ciò funzionerà bene per i primi prodotti, poiché questi prodotti hanno ProductID valori che si verificano per mappare a s validi CategoryID . Ma una volta che l'inizio ProductID inizia troppo grande, questa sovrapposizione coincidente di ProductID s e CategoryID s non si applica più.

Il terzo Click gestore eventi aggiorna i prodotti CategoryID nello stesso modo, ma invia l'aggiornamento al database usando il ProductsTableAdapter metodo predefinito Update . Questo Update metodo non esegue il wrapping della serie di comandi all'interno di una transazione, pertanto tali modifiche vengono apportate prima del primo errore di violazione dei vincoli di chiave esterna.

Per illustrare questo comportamento, visitare questa pagina tramite un browser. Inizialmente verrà visualizzata la prima pagina dei dati, come illustrato nella figura 8. Fare quindi clic sul pulsante Modifica categorie (WITH TRANSACTION). In questo modo si verificherà un postback e si tenterà di aggiornare tutti i valori dei prodotti CategoryID , ma comporterà una violazione del vincolo di chiave esterna (vedere la figura 9).

I prodotti vengono visualizzati in un controllo GridView paginabile

Figura 8: i prodotti vengono visualizzati in un controllo GridView paginabile (fare clic per visualizzare l'immagine a dimensioni complete)

Riassegnazione delle categorie comporta una violazione del vincolo di chiave esterna

Figura 9: Riassegnare le categorie genera una violazione del vincolo di chiave esterna (fare clic per visualizzare l'immagine full-size)

A questo punto, premere il pulsante Indietro del browser e quindi fare clic sul pulsante Aggiorna griglia. Dopo l'aggiornamento dei dati, è necessario visualizzare lo stesso output visualizzato nella figura 8. Vale a dire, anche se alcuni dei prodotti CategoryID sono stati modificati in valori legali e aggiornati nel database, sono stati eseguito il rollback quando si è verificata la violazione del vincolo di chiave esterna.

Provare a fare clic sul pulsante Modifica categorie (WITHOUT TRANSACTION). In questo modo si verificherà lo stesso errore di violazione dei vincoli 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 eseguito il rollback. Premere il pulsante Indietro del browser e quindi il pulsante Aggiorna griglia. Come illustrato nella figura 10, i CategoryID primi otto prodotti sono stati riassegnati. Ad esempio, nella figura 8 Chang aveva un CategoryID valore pari a 1, ma nella figura 10 è stato riassegnato a 2.

Alcuni valori CategoryID dei prodotti sono stati aggiornati mentre altri non erano

Figura 10: alcuni valori dei prodotti CategoryID sono stati aggiornati mentre altri non erano (fare clic per visualizzare l'immagine full-size)

Riepilogo

Per impostazione predefinita, i metodi TableAdapter non esegue il wrapping delle istruzioni di database eseguite nell'ambito di una transazione, ma con un po' di 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: BeginTransaction, CommitTransactione RollbackTransaction. È stato illustrato come usare questi metodi insieme a un try...catch blocco per creare una serie di istruzioni di modifica dei dati atomiche. In particolare, è stato creato il metodo in ProductsTableAdapter, che usa il UpdateWithTransaction modello di aggiornamento batch per eseguire le modifiche necessarie alle righe di un oggetto specificatoProductsDataTable. È stato aggiunto anche il metodo alla ProductsBLL classe BLL, che accetta un List valore di ProductID valori come input e chiama il DeleteProductsWithTransaction metodo Delete modello DB-Direct per ogni ProductIDoggetto . 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, la transazione viene eseguito il rollback, in caso contrario viene eseguito il commit.

Il passaggio 5 illustra l'effetto degli aggiornamenti batch transazionali rispetto agli aggiornamenti batch che non sono stati ignorati per l'uso di una transazione. Nelle tre esercitazioni successive si creerà la base descritta in questa esercitazione e si creeranno interfacce utente per l'esecuzione di aggiornamenti batch, eliminazioni e inserimenti.

Programmazione felice!

Altre informazioni

Per altre informazioni sugli argomenti illustrati in questa esercitazione, vedere le risorse seguenti:

Informazioni sull'autore

Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, ha lavorato con le tecnologie Microsoft Web dal 1998. Scott lavora come consulente indipendente, allenatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2,0 in 24 Ore. Può essere raggiunto a mitchell@4GuysFromRolla.com. o tramite il suo blog, che può essere trovato in http://ScottOnWriting.NET.

Grazie speciali

Questa serie di esercitazioni è stata esaminata da molti revisori utili. I revisori principali per questa esercitazione sono stati Dave Gardner, Hilton Giesenow e Teresa Murphy. Interessati a esaminare i prossimi articoli MSDN? In tal caso, lasciami una riga in mitchell@4GuysFromRolla.com.