Delen via


Aanpassingen in de database in een transactie verpakken (C#)

door Scott Mitchell

PDF downloaden

Deze handleiding is de eerste van vier en richt zich op het bijwerken, verwijderen en invoegen van gegevensbatches. In deze zelfstudie leert u hoe met databasetransacties batchwijzigingen kunnen worden uitgevoerd als een atomische bewerking, die ervoor zorgt dat alle stappen slagen of alle stappen mislukken.

Introductie

Zoals we hebben gezien met de zelfstudie Een overzicht van het invoegen, bijwerken en verwijderen van gegevens , biedt GridView ingebouwde ondersteuning voor bewerken en verwijderen op rijniveau. Met een paar klikken van de muis is het mogelijk om een rijke interface voor gegevenswijziging te maken zonder een regel code te schrijven, zolang u tevreden bent met bewerken en verwijderen per rij. In bepaalde scenario's is dit echter onvoldoende en moeten we gebruikers de mogelijkheid bieden om een batch records te bewerken of te verwijderen.

De meeste web-e-mailclients gebruiken bijvoorbeeld een raster om elk bericht weer te geven waarin elke rij een selectievakje bevat, samen met de gegevens van de e-mail (onderwerp, afzender enzovoort). Met deze interface kan de gebruiker meerdere berichten verwijderen door deze te controleren en vervolgens op de knop Geselecteerde berichten verwijderen te klikken. Een interface voor batchbewerking is ideaal in situaties waarin gebruikers vaak veel verschillende records bewerken. In plaats van de gebruiker te dwingen op Bewerken te klikken, de wijziging aan te brengen en vervolgens op Bijwerken te klikken voor elke record die moet worden gewijzigd, wordt elke rij met de bewerkingsinterface weergegeven. De gebruiker kan snel de set rijen wijzigen die moeten worden gewijzigd en deze wijzigingen vervolgens opslaan door op een knop Alles bijwerken te klikken. In deze reeks zelfstudies bekijken we hoe u interfaces maakt voor het invoegen, bewerken en verwijderen van batches met gegevens.

Bij het uitvoeren van batchbewerkingen is het belangrijk om te bepalen of het mogelijk moet zijn dat sommige bewerkingen in de batch slagen terwijl andere mislukken. Overweeg een batch-verwijderingsinterface: wat moet er gebeuren als de eerste geselecteerde record succesvol is verwijderd, maar de tweede mislukt, bijvoorbeeld door een schending van een foreign key-constraint? Moet het verwijderen van de eerste regel worden teruggedraaid of is het acceptabel dat de eerste regel verwijderd blijft?

Als u wilt dat de batchbewerking wordt behandeld als een atomische bewerking, één waarbij alle stappen slagen of alle stappen mislukken, moet de Data Access-laag worden uitgebreid om ondersteuning voor databasetransacties op te nemen. Databasetransacties garanderen atomiciteit voor de set INSERT, UPDATEen DELETE instructies die worden uitgevoerd onder de paraplu van de transactie en zijn een functie die wordt ondersteund door de meeste moderne databasesystemen.

In deze zelfstudie bekijken we hoe u de DAL kunt uitbreiden voor het gebruik van databasetransacties. In komende zelfstudies wordt onderzocht hoe je webpagina's implementeert voor het batchgewijs invoegen, bijwerken en verwijderen van interfaces. Laten we beginnen!

Opmerking

Bij het wijzigen van gegevens in een batchtransactie is atomiciteit niet altijd nodig. In sommige scenario's is het mogelijk acceptabel dat bepaalde gegevenswijzigingen slagen en andere in dezelfde batch mislukken, bijvoorbeeld bij het verwijderen van een set e-mailberichten van een web-e-mailclient. Als er halverwege het verwijderingsproces een databasefout optreedt, is het waarschijnlijk acceptabel dat deze records die zonder fouten worden verwerkt, worden verwijderd. In dergelijke gevallen hoeft de DAL niet te worden gewijzigd om databasetransacties te ondersteunen. Er zijn echter andere scenario's voor batchbewerkingen, waarbij atomiciteit essentieel is. Wanneer een klant haar geld van de ene bankrekening naar de andere verplaatst, moeten er twee bewerkingen worden uitgevoerd: het geld moet worden afgetrokken van de eerste rekening en vervolgens aan de tweede worden toegevoegd. Hoewel de bank het misschien niet erg vindt om de eerste stap te laten slagen, maar de tweede stap mislukt, zouden de klanten begrijpelijkerwijs overstuur zijn. Ik moedig u aan om door deze zelfstudie te werken en de verbeteringen aan de DAL te implementeren ter ondersteuning van databasetransacties, zelfs als u deze niet wilt gebruiken in de batch-invoeg-, bijwerk- en verwijderinterfaces die we in de volgende drie zelfstudies gaan bouwen.

Een overzicht van transacties

De meeste databases bevatten ondersteuning voor transacties, waardoor meerdere databaseopdrachten kunnen worden gegroepeerd in één logische werkeenheid. De databaseopdrachten waaruit een transactie bestaat, zijn gegarandeerd atomisch, wat betekent dat alle opdrachten mislukken of dat alles slaagt.

Over het algemeen worden transacties geïmplementeerd via SQL-instructies met behulp van het volgende patroon:

  1. Geef het begin van een transactie aan.
  2. Voer de SQL-instructies uit die de transactie vormen.
  3. Als er een fout optreedt in een van de instructies van stap 2, moet u de transactie terugdraaien.
  4. Als alle instructies uit stap 2 zonder fouten zijn voltooid, voert u de transactie door.

De SQL-instructies voor het maken, doorvoeren en terugdraaien van de transactie kunnen handmatig worden ingevoerd bij het schrijven van SQL-scripts of het maken van opgeslagen procedures, of via programmatische methoden die gebruikmaken van ADO.NET of de klassen in de System.Transactions naamruimte. In deze zelfstudie onderzoeken we alleen het beheren van transacties met behulp van ADO.NET. In een toekomstige zelfstudie bekijken we hoe we opgeslagen procedures in de Data Access-laag gebruiken. Op dat moment verkennen we de SQL-instructies voor het maken, terugdraaien en doorvoeren van transacties.

Opmerking

Met TransactionScope de klasse in de System.Transactions naamruimte kunnen ontwikkelaars programmatisch een reeks instructies verpakken binnen het bereik van een transactie en ondersteuning bieden voor complexe transacties waarbij meerdere bronnen betrokken zijn, zoals twee verschillende databases of zelfs heterogene typen gegevensarchieven, zoals een Microsoft SQL Server-database, een Oracle-database en een webservice. Ik heb besloten om ADO.NET transacties voor deze zelfstudie te gebruiken in plaats van de TransactionScope klasse, omdat ADO.NET specifieker is voor databasetransacties en in veel gevallen veel minder resource-intensief is. Bovendien maakt de TransactionScope klasse in bepaalde scenario's gebruik van de Microsoft Distributed Transaction Coordinator (MSDTC). De configuratie-, implementatie- en prestatieproblemen rond MSDTC maken het een eerder gespecialiseerd en geavanceerd onderwerp en buiten het bereik van deze zelfstudies.

Wanneer u met de SqlClient-provider in ADO.NET werkt, worden transacties geïnitieerd via een aanroep naar de SqlConnection klasse s methodeBeginTransaction, die een SqlTransaction object retourneert. De gegevenswijzigingsinstructies die de transactie vormen, worden binnen een try...catch blok geplaatst. Als er een fout optreedt in een instructie in het try blok, wordt de uitvoering overgedragen naar het catch blok waar de transactie kan worden teruggedraaid via de methode van SqlTransactionhet Rollback object. Als alle instructies zijn voltooid, wordt de transactie door een aanroep naar de methode van het SqlTransaction object Commit aan het einde van het try blok doorgevoerd. Het volgende codefragment illustreert dit patroon. Zie Databaseconsistentie onderhouden met transacties.

// 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;
}

De TableAdapters in een getypte dataset gebruiken standaard geen transacties. Om ondersteuning te bieden voor transacties moeten we de TableAdapter-klassen uitbreiden om aanvullende methoden op te nemen die gebruikmaken van het bovenstaande patroon om een reeks instructies voor gegevenswijziging uit te voeren binnen het bereik van een transactie. In stap 2 ziet u hoe u gedeeltelijke klassen kunt gebruiken om deze methoden toe te voegen.

Stap 1: De webpagina's voor werken met batchgegevens maken

Voordat we beginnen met het uitbreiden van de DAL ter ondersteuning van databasetransacties, nemen we eerst een ogenblik de tijd om de ASP.NET webpagina's te maken die we nodig hebben voor deze zelfstudie en de drie die volgen. Voeg eerst een nieuwe map toe met de naam BatchData en voeg vervolgens de volgende ASP.NET pagina's toe, waarbij elke pagina wordt gekoppeld aan de Site.master basispagina.

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

ASP.NET-pagina's toevoegen voor de SqlDataSource-Related-tutorials

Afbeelding 1: De ASP.NET pagina's voor de zelfstudies voor SqlDataSource-Related toevoegen

Net als bij de andere mappen gebruikt Default.aspx de SectionLevelTutorialListing.ascx gebruikerscontrole om de zelfstudies in de sectie weer te geven. Voeg daarom deze gebruikerscontrole toe aan Default.aspx door het vanuit de Solution Explorer naar de ontwerpweergave van de pagina te slepen.

Voeg de SectionLevelTutorialListing.ascx User Control toe aan Default.aspx

Afbeelding 2: Het gebruikersbesturingselement toevoegen SectionLevelTutorialListing.ascx aan Default.aspx (klik om de afbeelding op volledige grootte weer te geven)

Voeg tot slot deze vier pagina's toe als vermeldingen aan het Web.sitemap bestand. Voeg met name de volgende markeringen toe na het aanpassen van het siteoverzicht <siteMapNode>:

<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>

Neem na het bijwerken Web.sitemap even de moeite om de tutorialswebsite via een browser te bekijken. Het menu aan de linkerkant bevat nu items voor tutorials over het werken met batchgegevens.

Het siteoverzicht bevat nu vermeldingen voor de zelfstudies over het werken met batchgegevens

Afbeelding 3: De sitemap bevat nu vermeldingen voor de tutorials over batchgegevens.

Stap 2: de Data Access-laag bijwerken om databasetransacties te ondersteunen

Zoals we in de eerste tutorial hebben besproken, de Getypte DataSet in onze DAL bestaat uit DataTables en TableAdapters. De DataTables bevatten gegevens terwijl TableAdapters de functionaliteit bieden om gegevens uit de database in datatables te lezen, om de database bij te werken met wijzigingen in de DataTables, enzovoort. Zoals u weet, bieden de TableAdapters twee patronen voor het bijwerken van gegevens, die ik batch-update en DB-Direct noemde. Met het batch-updatepatroon wordt een DataSet, DataTable of verzameling DataRows doorgegeven aan de TableAdapter. Deze gegevens worden opgesomd en voor elke ingevoegde, gewijzigde of verwijderde rij, de InsertCommand, UpdateCommandof DeleteCommand wordt uitgevoerd. Met het DB-Direct patroon wordt de TableAdapter in plaats daarvan de waarden doorgegeven van de kolommen die nodig zijn voor het invoegen, bijwerken of verwijderen van één record. De db Direct-patroonmethode gebruikt vervolgens die doorgegeven waarden om de juiste InsertCommand, UpdateCommandof DeleteCommand instructie uit te voeren.

Ongeacht het gebruikte updatepatroon gebruiken de automatisch gegenereerde TableAdapters-methoden geen transacties. Standaard wordt elke invoeg-, bijwerk- of verwijderbewerking die door de TableAdapter wordt uitgevoerd, behandeld als één afzonderlijke bewerking. Stel dat het DB-Direct patroon wordt gebruikt door code in de BLL om tien records in de database in te voegen. Met deze code wordt de methode TableAdapter Insert tien keer aangeroepen. Als de eerste vijf invoegingen zijn geslaagd, maar de zesde heeft geresulteerd in een uitzondering, blijven de eerste vijf ingevoegde records in de database. Als het batch-updatepatroon wordt gebruikt om invoegingen, updates en verwijderingen uit te voeren in de ingevoegde, gewijzigde en verwijderde rijen in een gegevenstabel, en als de eerste wijzigingen zijn geslaagd maar er later een fout optreedt, zullen de eerdere wijzigingen die zijn voltooid in de database blijven.

In bepaalde scenario's willen we atomiciteit garanderen in een reeks wijzigingen. Hiervoor moeten we de TableAdapter handmatig uitbreiden door nieuwe methoden toe te voegen die de InsertCommand, UpdateCommanden DeleteCommand s onder de paraplu van een transactie uitvoeren. Bij het maken van een Data Access-laag hebben we gekeken naar het gebruik van gedeeltelijke klassen om de functionaliteit van de DataTables in de getypte gegevensset uit te breiden. Deze techniek kan ook worden gebruikt met TableAdapters.

De getypte gegevensset Northwind.xsd bevindt zich in de submap van App_Code de DAL map. Maak een submap in de map met de DAL naam TransactionSupport en voeg een nieuw klassebestand toe met de naam ProductsTableAdapter.TransactionSupport.cs (zie afbeelding 4). Dit bestand bevat de gedeeltelijke implementatie van de ProductsTableAdapter methoden voor het uitvoeren van gegevenswijzigingen met behulp van een transactie.

Voeg een map met de naam TransactionSupport en een klassebestand toe met de naam ProductsTableAdapter.TransactionSupport.cs

Afbeelding 4: Een map met de naam TransactionSupport en een klassebestand met de naam toevoegen ProductsTableAdapter.TransactionSupport.cs

Voer de volgende code in het ProductsTableAdapter.TransactionSupport.cs bestand in:

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();
        }
   }
}

Het partial trefwoord in de klassedeclaratie hier geeft aan de compiler aan dat de leden die binnen zijn toegevoegd, moeten worden toegevoegd aan de ProductsTableAdapter klasse in de NorthwindTableAdapters naamruimte. Noteer de using System.Data.SqlClient instructie bovenaan het bestand. Omdat TableAdapter is geconfigureerd voor het gebruik van de SqlClient-provider, wordt intern een SqlDataAdapter object gebruikt om de opdrachten uit te geven aan de database. Daarom moeten we de klasse gebruiken om de SqlTransaction transactie te starten en deze vervolgens vast te leggen of terug te draaien. Als u een ander gegevensarchief dan Microsoft SQL Server gebruikt, moet u de juiste provider gebruiken.

Deze methoden bieden de bouwstenen die nodig zijn voor het starten, terugdraaien en doorvoeren van een transactie. Ze worden gemarkeerd public, zodat ze kunnen worden gebruikt vanuit de ProductsTableAdapter, vanuit een andere klasse in de DAL, of vanuit een andere laag in de architectuur, zoals de BLL. BeginTransaction opent de Interne SqlConnection TableAdapter (indien nodig), begint de transactie en wijst deze toe aan de Transaction eigenschap en koppelt de transactie aan de interne SqlDataAdapter objecten SqlCommand . CommitTransaction en RollbackTransaction roep respectievelijk de Transaction object-s Commit en Rollback methoden aan voordat u het interne Connection object sluit.

Stap 3: Methoden toevoegen om gegevens bij te werken en te verwijderen onder de paraplu van een transactie

Als deze methoden zijn voltooid, zijn we klaar om methoden toe te voegen aan ProductsDataTable of aan de BLL die een reeks opdrachten uitvoeren onder de paraplu van een transactie. De volgende methode maakt gebruik van het Batch Update-patroon om een ProductsDataTable exemplaar bij te werken met behulp van een transactie. Er wordt een transactie gestart door de BeginTransaction methode aan te roepen en vervolgens een try...catch blok gebruikt om de instructies voor gegevenswijziging uit te geven. Als de aanroep naar de methode van het Adapter-object een uitzondering oplevert, wordt de uitvoering overgedragen naar het Update blok waar de transactie wordt teruggedraaid en de uitzondering opnieuw opgegooid. Zoals u weet, implementeert de Update methode het Batch Update-patroon door de rijen van de opgegeven ProductsDataTable rijen te inventariseren en de benodigde InsertCommand, UpdateCommanden DeleteCommand s uit te voeren. Als een van deze opdrachten een fout oplevert, wordt de transactie teruggedraaid, waardoor de vorige wijzigingen die zijn aangebracht tijdens de levensduur van de transactie ongedaan worden gemaakt. Als de Update instructie zonder fouten is voltooid, wordt de transactie volledig doorgevoerd.

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;
    }
}

Voeg de UpdateWithTransaction methode toe aan de ProductsTableAdapter klasse via de gedeeltelijke klasse in ProductsTableAdapter.TransactionSupport.cs. Deze methode kan ook worden toegevoegd aan de klasse Business Logic Layer ProductsBLL met enkele kleine syntacticale wijzigingen. Het trefwoord this in this.BeginTransaction(), this.CommitTransaction(), en this.RollbackTransaction() moet worden vervangen door Adapter (onthoud dat Adapter de naam is van een eigenschap van ProductsBLL van het type ProductsTableAdapter).

De UpdateWithTransaction methode maakt gebruik van het Batch Update-patroon, maar een reeks DB-Direct aanroepen kan ook worden gebruikt binnen het bereik van een transactie, zoals in de volgende methode wordt weergegeven. De DeleteProductsWithTransaction-methode accepteert als invoer een List<T> van het type int, dit zijn de ProductID die verwijderd moeten worden. De methode initieert de transactie via een aanroep naar BeginTransaction en doorloopt vervolgens in het try blok de opgegeven lijst die de DB-Direct patroonmethode Delete aanroept voor elke ProductID waarde. Als een van de aanroepen naar Delete mislukt, wordt de uitvoering overgedragen naar het catch blok waar de transactie wordt teruggedraaid en de uitzondering opnieuw gegooid. Als alle aanroepen Delete slagen, wordt de transactie doorgevoerd. Voeg deze methode toe aan de ProductsBLL klasse.

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;
    }
}

Transacties toepassen op meerdere TableAdapters

In deze tutorial onderzoekt de transactiegerelateerde code hoe meerdere instructies tegen ProductsTableAdapter kunnen worden behandeld als een atoomoperatie. Maar wat gebeurt er als meerdere wijzigingen in verschillende databasetabellen atomisch moeten worden uitgevoerd? Bij het verwijderen van een categorie willen we bijvoorbeeld eerst de huidige producten opnieuw toewijzen aan een andere categorie. Deze twee stappen die de producten opnieuw toewijzen en de categorie verwijderen, moeten worden uitgevoerd als een atomische bewerking. Maar de ProductsTableAdapter bevat alleen methoden voor het wijzigen van de Products tabel en de CategoriesTableAdapter bevat alleen methoden voor het wijzigen van de Categories tabel. Hoe kan een transactie beide TableAdapters omvatten?

Een optie is om een methode toe te voegen aan de CategoriesTableAdapter met de naam DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID), en deze methode laat een opgeslagen procedure aanroepen die zowel de producten opnieuw toewijst als de categorie verwijdert binnen het bereik van een transactie die is gedefinieerd in de opgeslagen procedure. In een toekomstige zelfstudie wordt uitgelegd hoe u transacties in opgeslagen procedures kunt starten, doorvoeren en terugdraaien.

Een andere optie is het maken van een helperklasse in de DAL die de DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) methode bevat. Met deze methode maakt u een exemplaar van de CategoriesTableAdapter en de ProductsTableAdapter en stelt u deze twee TableAdapters-eigenschappen Connection vervolgens in op hetzelfde SqlConnection exemplaar. Op dat moment zou een van de twee TableAdapters de transactie initiëren met een aanroep naar BeginTransaction. De TableAdapters-methoden voor het opnieuw toewijzen van de producten en het verwijderen van de categorie worden indien nodig aangeroepen in een try...catch blok waarbij de transactie is doorgevoerd of teruggedraaid.

Stap 4: deUpdateWithTransactionmethode toevoegen aan de bedrijfslogicalaag

In stap 3 hebben we een UpdateWithTransaction methode toegevoegd aan de ProductsTableAdapter in de DAL. We moeten een bijbehorende methode toevoegen aan de BLL. Hoewel de presentatielaag rechtstreeks naar de DAL kan gaan om de UpdateWithTransaction methode aan te roepen, hebben deze zelfstudies ernaar gestreefd een gelaagde architectuur te definiëren die de DAL van de presentatielaag isoleert. Daarom moeten we deze aanpak voortzetten.

Open het ProductsBLL klassebestand en voeg een methode toe met de naam UpdateWithTransaction waarmee eenvoudigweg de bijbehorende DAL-methode wordt aangeroepen. Er moeten nu twee nieuwe methoden zijn: ProductsBLLUpdateWithTransactiondie u zojuist hebt toegevoegd enDeleteProductsWithTransaction, die is toegevoegd in stap 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;
    }
}

Opmerking

Deze methoden bevatten niet het DataObjectMethodAttribute kenmerk dat is toegewezen aan de meeste andere methoden in de ProductsBLL klasse, omdat we deze methoden rechtstreeks vanuit de ASP.NET pagina's code-behind-klassen aanroepen. Zoals u weet, DataObjectMethodAttribute wordt gebruikt om te markeren welke methoden moeten worden weergegeven in de wizard Gegevensbron configureren van ObjectDataSource en onder welk tabblad (SELECT, UPDATE, INSERT of DELETE). Omdat gridView geen ingebouwde ondersteuning voor batchbewerking of -verwijdering mist, moeten we deze methoden programmatisch aanroepen in plaats van de declaratieve benadering zonder code te gebruiken.

Stap 5: Databasegegevens atomisch bijwerken vanuit de presentatielaag

Ter illustratie van het effect dat de transactie heeft bij het bijwerken van een batch records, gaan we een gebruikersinterface maken waarin alle producten in een GridView worden weergegeven en een knopwebbesturingselement bevat dat, wanneer erop wordt geklikt, de waarden van de producten CategoryID opnieuw toewijst. In het bijzonder wordt de toewijzing van de categorie voortgezet, zodat aan de eerste verschillende producten een geldige CategoryID waarde wordt toegewezen, terwijl aan andere producten doelbewust een niet-bestaande CategoryID waarde wordt toegewezen. Als we proberen de database bij te werken met een product waarvan CategoryID niet overeenkomt met een bestaande categorie CategoryID, treedt er een schending van een buitenlandse sleutelbeperking op en zal er een uitzondering optreden. Wat we in dit voorbeeld zien, is dat wanneer u een transactie gebruikt, de uitzondering die wordt veroorzaakt door een schending van de referentiële sleutelbeperking ervoor zorgt dat de vorige geldige CategoryID wijzigingen worden teruggedraaid. Wanneer u echter geen transactie gebruikt, blijven de wijzigingen in de oorspronkelijke categorieën behouden.

Begin met het openen van de Transactions.aspx pagina in de BatchData map en sleep een GridView van de gereedschapskist naar de ontwerper. Stel het ID in op Products en, van de smart tag, bind het aan een nieuwe ObjectDataSource met de naam ProductsDataSource. Configureer de ObjectDataSource om de gegevens op te halen uit de ProductsBLL klasse en de GetProducts methode. Dit is een alleen-lezen GridView, dus stel de vervolgkeuzelijsten in de tabbladen UPDATE, INSERT en DELETE in op (Geen) en klik op Afronden.

Afbeelding 5: De ObjectDataSource configureren voor het gebruik van de methode ProductsBLL Class s GetProducts

Afbeelding 5: Afbeelding 5: De ObjectDataSource configureren om de ProductsBLL methode klasse GetProducts te gebruiken (klik om de afbeelding op volledige grootte weer te geven)

Stel de Drop-Down-lijsten in de tabbladen UPDATE, INSERT en DELETE in op (Geen)

Afbeelding 6: Stel de Drop-Down-lijsten in de tabbladen UPDATE, INSERT en DELETE in op (Geen) (Klik om de volledige afbeelding weer te geven)

Nadat u de wizard Gegevensbron configureren hebt voltooid, maakt Visual Studio BoundFields en een CheckBoxField voor de productgegevensvelden. Verwijder al deze velden, behalve ProductID, ProductName, CategoryID, en CategoryName en hernoem de ProductName en CategoryName BoundFields HeaderText eigenschappen in respectievelijk Product en Categorie. Schakel de optie "Paginering inschakelen" in binnen het smart tag. Nadat u deze wijzigingen hebt aangebracht, moeten de declaratieve markeringen van GridView en ObjectDataSource er als volgt uitzien:

<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>

Voeg vervolgens drie knopwebbesturingselementen toe boven rasterweergave. Stel de Tekst-eigenschap van de eerste knop in op Vernieuw raster, de tweede op Categorieën wijzigen (MET TRANSACTIE) en de derde op Categorieën wijzigen (ZONDER TRANSACTIE).

<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>

Op dit moment moet de ontwerpweergave in Visual Studio er ongeveer uitzien als de schermafbeelding die in afbeelding 7 wordt weergegeven.

De pagina bevat een gridview en drie knopwebbesturingselementen

Afbeelding 7: De pagina bevat een rasterweergave en drie knopwebbesturingselementen (klik om de afbeelding volledig weer te geven)

Maak eventhandlers voor elk van de drie knop Click-gebeurtenissen en gebruik de volgende code:

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();
}

Met de gebeurtenis-handler van de verversknop Click worden de gegevens eenvoudig opnieuw aan de GridView gekoppeld door de GridView Products methode DataBind aan te roepen.

Met de tweede gebeurtenishandler worden de producten CategoryID opnieuw toegewezen en wordt de nieuwe transactiemethode van de BLL gebruikt om de database-updates uit te voeren onder de paraplu van een transactie. Houd er rekening mee dat elke product s CategoryID willekeurig is ingesteld op dezelfde waarde als de waarde ervan ProductID. Dit werkt prima voor de eerste paar producten, omdat deze producten ProductID-waarden hebben die toevallig overeenkomen met geldige CategoryIDs. Maar zodra de ProductID s te groot worden, is deze samenvallende overlapping van ProductID s en CategoryID s niet meer van toepassing.

De derde Click gebeurtenis-handler werkt de producten CategoryID op dezelfde manier bij, maar verzendt de update naar de database met behulp van de ProductsTableAdapter standaardmethode Update . Met deze Update methode worden de reeks opdrachten niet opgenomen binnen een transactie, dus deze wijzigingen worden aangebracht voordat een fout vanwege schending van een vreemde-sleutelbeperking wordt gedetecteerd en zullen blijven bestaan.

Als u dit gedrag wilt demonstreren, gaat u naar deze pagina via een browser. In eerste instantie ziet u de eerste pagina met gegevens, zoals weergegeven in afbeelding 8. Klik vervolgens op de knop Categorieën wijzigen (MET TRANSACTIE). Dit zal een postback-operatie veroorzaken en zal proberen alle CategoryID-waarden van de producten bij te werken, maar resulteert in een schending van een buitenlandse sleutelregel (zie figuur 9).

De producten worden weergegeven in een paginabare rasterweergave

Afbeelding 8: De producten worden weergegeven in een paginabare rasterweergave (klik hier om de volledige afbeelding weer te geven)

Het opnieuw toewijzen van de categorieën resulteert in een overtreding van een foreign key-beperking.

Afbeelding 9: Het opnieuw toewijzen van de categorieën leidt tot een schending van een referentiesleutelbeperking (klik om de afbeelding op volledige grootte weer te geven)

Druk nu op de knop Vorige in uw browser en druk dan op de knop Ververs raster. Wanneer u de gegevens vernieuwt, ziet u exact dezelfde uitvoer als in afbeelding 8. Dat wil zeggen, ook al zijn sommige producten CategoryID gewijzigd in geldige waarden en bijgewerkt zijn in de database, werden ze herroepen toen er een schending van de refererende sleutelbeperking optrad.

Klik nu op de knop Categorieën wijzigen (ZONDER TRANSACTIE). Dit leidt tot dezelfde schending van de foreign key-beperking (zie afbeelding 9), maar deze keer worden die producten waarvan CategoryID de waarden zijn gewijzigd in een geldige waarde, niet teruggedraaid. Druk op de Terugknop van uw browser en daarna op de knop Raster verversen. Zoals in afbeelding 10 wordt weergegeven, zijn de CategoryID s van de eerste acht producten opnieuw toegewezen. In afbeelding 8 had Chang bijvoorbeeld een CategoryID waarde van 1, maar in afbeelding 10 is het opnieuw toegewezen aan 2.

Sommige producten categorie-ID-waarden zijn bijgewerkt, terwijl anderen dat niet zijn

Afbeelding 10: Sommige waarden van producten CategoryID zijn bijgewerkt terwijl andere niet zijn (klik om de afbeelding op volledige grootte weer te geven)

Samenvatting

De TableAdapter-methoden verpakken standaard niet de uitgevoerde database-instructies binnen het bereik van een transactie, maar met weinig werk kunnen we methoden toevoegen waarmee een transactie wordt gemaakt, doorgevoerd en teruggedraaid. In deze zelfstudie hebben we drie methoden gemaakt in de ProductsTableAdapter klasse: BeginTransaction, CommitTransactionen RollbackTransaction. We hebben gezien hoe we deze methoden samen met een try...catch blok kunnen gebruiken om een reeks atomische instructies voor gegevenswijziging te maken. In het bijzonder hebben we de UpdateWithTransaction-methode gemaakt in het ProductsTableAdapter-patroon dat gebruikmaakt van het Batch Update-patroon om de benodigde wijzigingen aan te brengen in de rijen van een opgegeven ProductsDataTable. We hebben ook de DeleteProductsWithTransaction-methode toegevoegd aan de ProductsBLL-klasse in de BLL, die een List van ProductID waarden accepteert als invoer en de DB-Direct-patroonmethode Delete aanroept voor elke ProductID. Beide methoden beginnen met het maken van een transactie en vervolgens het uitvoeren van de instructies voor het wijzigen van gegevens binnen een try...catch blok. Als er een uitzondering optreedt, wordt de transactie teruggedraaid, anders wordt deze doorgevoerd.

Stap 5 illustreerde het effect van transactionele batch-updates versus batchupdates die een transactie niet gebruikten. In de volgende drie zelfstudies bouwen we voort op de basis die in deze zelfstudie is gelegd en maken we gebruikersinterfaces voor het uitvoeren van batchupdates, verwijderingen en invoegingen.

Veel plezier met programmeren!

Meer lezen

Raadpleeg de volgende bronnen voor meer informatie over de onderwerpen die in deze zelfstudie worden besproken:

Over de auteur

Scott Mitchell, auteur van zeven ASP/ASP.NET-boeken en oprichter van 4GuysFromRolla.com, werkt sinds 1998 met Microsoft-webtechnologieën. Scott werkt als onafhankelijk consultant, trainer en schrijver. Zijn laatste boek is Sams Teach Yourself ASP.NET 2.0 in 24 uur. Hij kan worden bereikt op mitchell@4GuysFromRolla.com.

Speciale dank aan

Deze tutorialreeks is beoordeeld door veel behulpzame beoordelers. Hoofdrecensenten voor deze zelfstudie waren Dave Gardner, Hilton Giesenow en Teresa Murphy. Bent u geïnteresseerd in het bekijken van mijn aanstaande MSDN-artikelen? Zo ja, laat iets van je horen via mitchell@4GuysFromRolla.com.