Megosztás a következőn keresztül:


Adatbázis-módosítások burkolása egy tranzakción belül (VB)

által Scott Mitchell

PDF letöltése

Ez az oktatóanyag a négy közül az első, amely adatkötegek frissítését, törlését és beszúrását vizsgálja. Ebben az oktatóanyagban megtanuljuk, hogyan teszik lehetővé az adatbázis-tranzakciók a kötegmódosítások atomi műveletként történő végrehajtását, ami biztosítja, hogy az összes lépés sikeres legyen, vagy az összes lépés sikertelen legyen.

Bevezetés

Mint láttuk, az Adatok beszúrása, frissítése és törlése oktatóanyaggal kezdődően a GridView beépített támogatást nyújt a sorszintű szerkesztéshez és törléshez. Az egér néhány kattintásával kódsor írása nélkül is létrehozhat egy gazdag adatmódosítási felületet, amennyiben elégedett a soronkénti szerkesztéssel és törléssel. Bizonyos esetekben azonban ez nem elegendő, és lehetővé kell tenni, hogy a felhasználók szerkeszthessenek vagy törölhessenek egy rekordköteget.

A legtöbb webes levelezőprogram például rács segítségével listáz minden egyes üzenetet, ahol minden sor tartalmaz egy jelölőnégyzetet az e-mail adataival (tárgy, feladó stb.). Ez a felület lehetővé teszi, hogy a felhasználó több üzenetet is töröljön, ha ellenőrzi őket, majd a Kijelölt üzenetek törlése gombra kattint. A kötegszerkesztési felület ideális olyan helyzetekben, amikor a felhasználók gyakran sok különböző rekordot szerkesztenek. Ahelyett, hogy arra kényszeríti a felhasználót, hogy a Szerkesztés gombra kattintson, végezze el a módosítást, majd kattintson a Módosítandó rekordok Frissítés gombjára, a kötegszerkesztő felület minden sort a szerkesztési felületével jelenít meg. A felhasználó gyorsan módosíthatja a módosítani kívánt sorok készletét, majd az Összes frissítése gombra kattintva mentheti ezeket a módosításokat. Ebben az oktatóanyag-készletben azt vizsgáljuk meg, hogyan hozhat létre interfészeket adatkötegek beszúrásához, szerkesztéséhez és törléséhez.

A kötegműveletek végrehajtásakor fontos meghatározni, hogy lehetséges-e, hogy a köteg egyes műveletei sikeresek legyenek, míg mások meghiúsulnak. Gondoljunk egy tömeges törlési felületre - mi történjen, ha az első kijelölt rekordot sikeresen törlik, de a második, mondjuk egy idegenkulcs-korlátozás megsértése miatt, meghiúsul? Vissza kell-e állítani az első rekord törlését, vagy elfogadható az első rekord törlése?

Ha azt szeretné, hogy a kötegművelet atomi műveletként legyen kezelve, amely vagy az összes lépés sikeres, vagy az összes lépés sikertelen, akkor az adatelérési réteget ki kell egészíteni az adatbázis-tranzakciók támogatásának biztosításához. Az adatbázis-tranzakciók garantálják a tranzakció keretében végrehajtott INSERT, UPDATE és DELETE utasításkészletek atomiságát, ami egy olyan funkció, amelyet a legtöbb modern adatbázisrendszer támogat.

Ebben az oktatóanyagban azt vizsgáljuk meg, hogyan terjesztheti ki a DAL-t az adatbázis-tranzakciók használatára. A következő oktatóanyagok azt vizsgálják, hogyan lehet weblapokat implementálni tömeges beszúrási, frissítési és törlési felületekhez. Lássunk hozzá!

Megjegyzés:

A kötegtranzakció adatainak módosításakor nem mindig van szükség atomiságra. Bizonyos esetekben elfogadható lehet, hogy egyes adatmódosítások sikeresek, mások pedig ugyanabban a kötegben meghiúsulnak, például ha egy webalapú levelezőügyfélről töröl egy e-mail-készletet. Ha a törlési folyamat közepén adatbázishiba lép fel, akkor valószínűleg elfogadható, hogy a hiba nélkül feldolgozott rekordok továbbra is törlődnek. Ilyen esetekben a DAL-t nem kell módosítani az adatbázis-tranzakciók támogatásához. Vannak azonban más kötegműveleti forgatókönyvek is, ahol az atomiság létfontosságú. Amikor egy ügyfél az egyik bankszámláról a másikra helyezi át a pénzét, két műveletet kell végrehajtani: az összeget le kell vonni az első számláról, majd hozzá kell adni a másodikhoz. Bár a bank nem bánja, hogy az első lépés sikeres, de a második lépés meghiúsul, az ügyfelek érthetően ideges. Arra biztatlak, hogy dolgozd végig ezt az oktatóanyagot, és implementáld a fejlesztéseket annak érdekében, hogy a DAL támogassa az adatbázis-tranzakciókat, még akkor is, ha nem tervezed használni azokat a köteg beszúrási, frissítési és törlési felületeken, amelyeket a következő három oktatóanyagban fogunk építeni.

A tranzakciók áttekintése

A legtöbb adatbázis támogatja a tranzakciókat, amelyek lehetővé teszik, hogy több adatbázis-parancs egyetlen logikai egységbe legyen csoportosítva. A tranzakciót alkotó adatbázis-parancsok garantáltan atomiak, ami azt jelenti, hogy vagy az összes parancs sikertelen lesz, vagy mindegyik sikeres lesz.

A tranzakciók általában AZ SQL-utasításokon keresztül valósulnak meg a következő mintával:

  1. Egy tranzakció kezdetét jelzi.
  2. Hajtsa végre a tranzakciót alkotó SQL-utasításokat.
  3. Ha a 2. lépés egyik utasításában hiba lép fel, visszaállítsa a tranzakciót.
  4. Ha a 2. lépés összes utasítása hiba nélkül befejeződik, véglegesítse a tranzakciót.

A tranzakció létrehozásához, véglegesítéséhez és visszaállításához használt SQL-utasítások manuálisan írhatók be SQL-szkriptek írásakor vagy tárolt eljárások létrehozásakor, vagy programozott módon, ADO.NET vagy a névtér osztályainak System.Transactionshasználatával. Ebben az oktatóanyagban csak a tranzakciók ADO.NET használatával történő kezelését vizsgáljuk meg. Egy későbbi oktatóanyagban áttekintjük, hogyan használhatjuk a tárolt eljárásokat az adatelérési rétegben, amely során megismerjük a tranzakciók létrehozására, visszaállítására és véglegesítésére vonatkozó SQL-utasításokat. Addig is tekintse meg a Tranzakciók kezelése az SQL Server tárolt eljárásaiban című témakört további információért.

Megjegyzés:

A TransactionScope névtér osztálya lehetővé teszi a System.Transactions fejlesztők számára, hogy programozott módon becsomagoljanak egy tranzakció hatókörébe egy utasítássorozatot, és támogatást nyújtanak olyan összetett tranzakciókhoz, amelyek több forrást is érintenek, például két különböző adatbázist vagy akár heterogén típusú adattárat, például Microsoft SQL Server-adatbázist, Oracle-adatbázist és webszolgáltatást. Úgy döntöttem, hogy ADO.NET tranzakciókat használok ehhez az oktatóanyaghoz az TransactionScope osztály helyett, mert ADO.NET az adatbázis-tranzakciókra jellemzőbb, és sok esetben sokkal kevésbé erőforrás-igényes. Emellett bizonyos esetekben az osztály a TransactionScope Microsoft Distributed Transaction Coordinator (MSDTC) szolgáltatást használja. Az MSDTC konfigurációs, megvalósítási és teljesítményproblémái meglehetősen speciális és speciális témakört alkotnak, és túlmutatnak ezen oktatóanyagok hatókörén.

Amikor az SqlClient-szolgáltatóval dolgozik a ADO.NET, a tranzakciók az s osztály metódusánakSqlConnection hívásával indulnak el, amely egyBeginTransaction objektumot ad vissza.SqlTransaction A tranzakciót alkotó adatmódosítási utasítások blokkban try...catch vannak elhelyezve. Ha hiba történik a blokk egyik utasításában, a try végrehajtás arra a catch blokkra kerül, ahol a tranzakció az SqlTransactionRollback visszaállítható. Ha az összes utasítás sikeresen befejeződött, a blokk végén az SqlTransactionobjektum metódusánakCommit hívása try véglegesíti a tranzakciót. Az alábbi kódrészlet ezt a mintát szemlélteti.

' 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

Alapértelmezés szerint a Tipizált adatkészlet TableAdapterei nem használnak tranzakciókat. A tranzakciók támogatásához ki kell bővítenünk a TableAdapter-osztályokat, hogy a fenti mintát használó további módszereket is belefoglaljunk egy tranzakció hatókörébe tartozó adatmódosítási utasítások sorozatának végrehajtásához. A 2. lépésben látni fogjuk, hogyan adhat hozzá részleges osztályokat ezekhez a módszerekhez.

1. lépés: A kötegelt adatoldalakkal dolgozó weboldalak létrehozása

Mielőtt elkezdenénk felfedezni, hogyan bővíthetjük a DAL-t az adatbázis-tranzakciók támogatásához, először szánjunk egy kis időt a ASP.NET weblapok létrehozására, amelyekre szükségünk lesz ehhez az oktatóanyaghoz és a következő háromhoz. Először adjon hozzá egy új mappát, BatchData majd adja hozzá a következő ASP.NET lapokat, és társítsa az egyes lapokat a Site.master mesterlaphoz.

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

Az SqlDataSource-Related oktatóanyagok ASP.NET lapjainak hozzáadása

1. ábra: A SqlDataSource-Related oktatóanyagok ASP.NET lapjainak hozzáadása

A többi mappához Default.aspx hasonlóan a SectionLevelTutorialListing.ascx Felhasználói vezérlővel is listázhatja a szakaszon belüli oktatóanyagokat. Ezért húzza ezt a felhasználói vezérlőt a Megoldáskezelőből a lap tervezési nézetébe.

Adja hozzá a SectionLevelTutorialListing.ascx felhasználói vezérlőt a Default.aspx

2. ábra: Adja hozzá a SectionLevelTutorialListing.ascx felhasználói vezérlőt Default.aspx (kattintson ide a teljes méretű kép megtekintéséhez)

Végül adja hozzá ezt a négy lapot bejegyzésként a Web.sitemap fájlhoz. Pontosabban adja hozzá a következő elemet a webhelytérkép testreszabása után <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>

A frissítés Web.sitemapután szánjon egy kis időt az oktatóanyagok webhelyének megtekintésére egy böngészőben. A bal oldali menü mostantól tartalmazza a kötegelt adat-oktatóanyagok használatához szükséges elemeket.

A webhelytérkép már tartalmazza a kötegelt adatokkal dolgozó oktatóanyagokhoz tartozó bejegyzéseket

3. ábra: A webhelytérkép most már tartalmaz bejegyzéseket a kötegelt adatokkal kapcsolatos oktatóanyagokhoz

2. lépés: Az adatelérési réteg frissítése az adatbázis-tranzakciók támogatásához

Ahogy az első oktatóanyagban, az Adatelérési réteg létrehozása című oktatóanyagban is szó esett, a DAL-ban szereplő Typed DataSet DataTables és TableAdapters összetevőkből áll. A DataTables az adatokat tárolja, míg a TableAdapters biztosítja az adatbázisból a DataTables-ba való adatolvasást, az adatbázis adattáblákon végzett módosításokkal való frissítését és így tovább. Ne feledje, hogy a TableAdapters két mintát biztosít az adatok frissítéséhez, amelyeket Batch Update-nek és DB-Direct-nek neveztem. A Batch Update mintával a TableAdapter adatkészletet, DataTable-t vagy DataRows-gyűjteményt ad át. Ezek az adatok számba lesznek sorolva, és minden beszúrt, módosított vagy törölt sor esetében a InsertCommand, UpdateCommandvagy DeleteCommand végrehajtásra kerül. A DB-Direct mintával a TableAdapter helyett az egyetlen rekord beszúrásához, frissítéséhez vagy törléséhez szükséges oszlopok értékei lesznek átadva. A közvetlen ADATBÁZIS-mintametódus ezután ezeket az átadott értékeket használja a megfelelő InsertCommand, UpdateCommandvagy DeleteCommand utasítás végrehajtásához.

A TableAdapters automatikusan létrehozott metódusai a használt frissítési mintától függetlenül nem használnak tranzakciókat. Alapértelmezés szerint a TableAdapter által végrehajtott beszúrások, frissítések vagy törlések egyetlen különálló műveletként lesznek kezelve. Tegyük fel például, hogy a BLL-ben található kód a DB-Direct mintát használja tíz rekord beszúrásához az adatbázisba. Ez a kód tízszer hívja meg a TableAdapter s Insert metódust. Ha az első öt beszúrás sikeres, de a hatodik kivételt eredményez, az első öt beszúrt rekord az adatbázisban marad. Hasonlóképpen, ha a Batch-frissítési mintát használják a DataTable beszúrt, módosított és törölt sorainak beszúrására, frissítésére és törlésére, ha az első néhány módosítás sikeres volt, de egy későbbi hibát észlelt, a korábban végrehajtott módosítások az adatbázisban maradnak.

Bizonyos esetekben biztosítani szeretnénk az atomiságot egy sor módosítás során. Ehhez manuálisan ki kell bővítenünk a TableAdaptert úgy, hogy új metódusokat adunk hozzá, amelyek végrehajtják a InsertCommand, UpdateCommandés DeleteCommand az s függvényt egy tranzakció esernyője alatt. Az adatelérési réteg létrehozásakorrészleges osztályok használatával bővítettük a DataTables funkcióit a Gépelt adatkészleten belül. Ez a technika a TableAdapters használatával is használható.

A Gépelt adatkészlet Northwind.xsd a App_Code mappa DAL almappájában található. Hozzon létre egy almappát a DAL névvel ellátott TransactionSupport mappában, és adjon hozzá egy új osztályfájlt ( ProductsTableAdapter.TransactionSupport.vb lásd a 4. ábrát). Ez a fájl tartalmazza a tranzakcióval végzett adatmódosítások végrehajtásának módszereit tartalmazó részleges implementációt ProductsTableAdapter .

Adjon hozzá egy TransactionSupport nevű mappát és egy ProductsTableAdapter.TransactionSupport.vb nevű osztályfájlt

4. ábra: Névvel ellátott TransactionSupport mappa és elnevezett osztályfájl hozzáadása ProductsTableAdapter.TransactionSupport.vb

Írja be a következő kódot a ProductsTableAdapter.TransactionSupport.vb fájlba:

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

Az Partial kulcsszó az osztálydeklarációban azt jelzi a fordítónak, hogy a belül hozzáadott tagokat hozzá kell adni a ProductsTableAdapter osztályhoz a NorthwindTableAdapters névtérben. Jegyezze fel a Imports System.Data.SqlClient fájl tetején található utasítást. Mivel a TableAdapter úgy lett konfigurálva, hogy az SqlClient-szolgáltatót használja, belsőleg egy SqlDataAdapter objektummal adja ki a parancsait az adatbázisnak. Következésképpen az osztályt kell használnunk a SqlTransaction tranzakció elindításához, majd véglegesítéséhez vagy visszaállításához. Ha a Microsoft SQL Serveren kívül más adattárat használ, a megfelelő szolgáltatót kell használnia.

Ezek a módszerek biztosítják a tranzakciók elindításához, visszaállításához és véglegesítéséhez szükséges építőelemeket. A Public jelölésük lehetővé teszi, hogy őket a ProductsTableAdapter-ben belülről, a DAL egy másik osztályából vagy az architektúra egy másik rétegéből, például a BLL-ből használják. BeginTransaction megnyitja a TableAdapter s belső SqlConnection objektumot (ha szükséges), elindítja a tranzakciót, és hozzárendeli a Transaction tulajdonsághoz, és csatolja a tranzakciót a belső SqlDataAdapter objektumokhoz SqlCommand . CommitTransaction és RollbackTransaction az Transaction objektum Commit és Rollback metódusait hívja meg, mielőtt bezárja a belső Connection objektumot.

3. lépés: Metódusok hozzáadása az adatok frissítéséhez és törléséhez egy tranzakció esernyője alatt

Ha ezek a metódusok befejeződnek, készen állunk olyan metódusok hozzáadására vagy a BLL-hez ProductsDataTable , amelyek parancsok sorozatát hajtják végre egy tranzakció ernyője alatt. Az alábbi módszer a Batch Update-mintát használja egy ProductsDataTable példány tranzakcióval történő frissítéséhez. A BeginTransaction metódus meghívásával indít el egy tranzakciót, majd a Try...Catch blokkot használva adja ki az adatmódosítási utasításokat. Ha az Adapter objektum metódusának hívása Update kivételt eredményez, a végrehajtás arra a catch blokkra kerül át, ahol a tranzakció vissza lesz állítva, és a kivétel újra lesz dobva. Ne feledje, hogy a módszer a Update Batch Update-mintát a megadott ProductsDataTable sorok számbavételével és a szükséges InsertCommand, UpdateCommandés DeleteCommand s végrehajtásával valósítja meg. Ha a parancsok bármelyike hibát eredményez, a rendszer visszaállítja a tranzakciót, és visszavonja a tranzakció élettartama során végrehajtott korábbi módosításokat. Ha az Update utasítás hiba nélkül fejeződik be, a tranzakció teljes egészében véglegesítésre kerül.

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

Adja hozzá a UpdateWithTransaction metódust az ProductsTableAdapter osztályhoz a ProductsTableAdapter.TransactionSupport.vb részleges osztályán keresztül. Másik lehetőségként ez a módszer hozzáadható az Üzleti logikai réteg s ProductsBLL osztályához néhány kisebb szintaktikai módosítással. Nevezetesen a kulcsszót , Meés le kell cserélni a következőre Me.BeginTransaction() : (emlékezzünk vissza, hogy Me.CommitTransaction() ez egy tulajdonság Me.RollbackTransaction() neve a típusbanAdapter).AdapterProductsBLLProductsTableAdapter

A UpdateWithTransaction módszer a Batch Update mintát használja, de egy tranzakció hatókörén belül DB-Direct hívások sorozata is használható, ahogyan az alábbi módszer mutatja. A DeleteProductsWithTransaction metódus List(Of T) típusú Integer bemenetet fogad el, amely a törlendő ProductID-ek. A metódus egy hívással BeginTransaction kezdeményezi a tranzakciót, majd a Try blokkban végigmegy a megadott listán, és az egyes Delete értékeknél meghívja a DB-Direct minta ProductID metódusát. Ha bármelyik Delete hívás meghiúsul, az irányítás átkerül a Catch blokkba, ahol a tranzakció visszafordításra kerül, és a kivétel újra dobódik. Ha az összes Delete hívás sikerül, akkor a tranzakció végleges lesz. Adja hozzá ezt a metódust az ProductsBLL osztályhoz.

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

Tranzakciók alkalmazása több adattábla-összekötőn

Az oktatóanyagban megvizsgált tranzakcióval kapcsolatos kód lehetővé teszi, hogy a rendszer több utasítást is használjon az ProductsTableAdapter atomi műveletként való kezeléshez. De mi a teendő, ha több módosítást kell végrehajtani a különböző adatbázistáblákon atomilag? Ha például töröl egy kategóriát, érdemes lehet az aktuális termékeit egy másik kategóriához rendelni. A termékek újbóli hozzárendelésének és a kategória törlésének két lépését atomi műveletként kell végrehajtani. De csak a ProductsTableAdapter tábla módosítására Products szolgáló módszereket tartalmazza, és csak a CategoriesTableAdapterCategories tábla módosítására szolgáló módszereket. Tehát hogyan terjedhet ki egy tranzakció mindkét TableAdaptersre?

Az egyik lehetőség az, hogy hozzáad egy metódust a CategoriesTableAdapter névvel ellátotthoz DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) , és a metódus meghív egy tárolt eljárást, amely mindkettő újra hozzárendeli a termékeket, és törli a kategóriát a tárolt eljárásban meghatározott tranzakció hatókörén belül. Egy későbbi oktatóanyagban áttekintjük, hogyan kezdheti el, véglegesítheti és visszaállíthatja a tranzakciókat a tárolt eljárásokban.

Egy másik lehetőség a metódust tartalmazó segédosztály létrehozása a DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) DAL-ban. Ez a metódus létrehoz egy példányt CategoriesTableAdapter és ProductsTableAdapter, majd a két TableAdapters Connection tulajdonságot ugyanarra a SqlConnection példányra állítja. Ezen a ponton a két TableAdapters bármelyike kezdeményezné a tranzakciót a következő hívással BeginTransaction: . A termékek újbóli hozzárendelésére és a kategória törlésére szolgáló TableAdapters metódusokat a rendszer egy Try...Catch blokkban hívja meg, amelyben a tranzakció lekötött vagy szükség szerint vissza lett állítva.

4. lépés: A metódus hozzáadása azUpdateWithTransactionüzleti logikai réteghez

A 3. lépésben hozzáadtunk egy UpdateWithTransaction metódust a ProductsTableAdapter DAL-hez. Hozzá kell adnunk egy megfelelő módszert a BLL-hez. Bár a bemutató réteg közvetlenül a DAL-ra is meghívhatta a UpdateWithTransaction metódust, ezek az oktatóanyagok arra törekedtek, hogy olyan rétegzett architektúrát határozzanak meg, amely a DAL-t elszigeteli a bemutató rétegtől. Ezért fontos számunkra, hogy továbbra is ezt a megközelítést alkalmazzuk.

Nyissa meg az ProductsBLL osztályfájlt, és adjon hozzá egy metódust UpdateWithTransaction , amely egyszerűen lehívja a megfelelő DAL metódust. A következőben két új metódusnak ProductsBLLkell lennie: UpdateWithTransactionaz imént hozzáadott, és DeleteProductsWithTransactiona 3. lépésben hozzáadott metódusnak.

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

Megjegyzés:

Ezek a metódusok nem tartalmazzák a DataObjectMethodAttribute osztály legtöbb más metódusához hozzárendelt ProductsBLL attribútumot, mivel ezeket a metódusokat közvetlenül az ASP.NET oldalak kód mögötti osztályaiból fogjuk meghívni. Ne feledje, hogy a DataObjectMethodAttribute jelzi, mely metódusok jelenjenek meg az ObjectDataSource Adatforrás konfigurálása varázslóban, és melyik lapon (SELECT, UPDATE, INSERT vagy DELETE). Mivel a GridView nem támogatja a kötegelt szerkesztést vagy törlést, a kódmentes deklaratív megközelítés helyett programozott módon kell meghívnunk ezeket a metódusokat.

5. lépés: Adatbázisadatok atomi frissítése a bemutató rétegből

Ha azt szeretné szemléltetni, hogy a tranzakció milyen hatással van egy rekordköteg frissítésekor, hozzunk létre egy felhasználói felületet, amely felsorolja a GridView összes termékét, és tartalmaz egy gombos webvezérlőt, amely kattintáskor újra hozzárendeli a termékek CategoryID értékeit. A kategória-újraosztás különösen úgy fog haladni, hogy az első néhány termék érvényes CategoryID értéket kap, míg mások szándékosan nem létező CategoryID értéket kapnak. Ha olyan termékkel kíséreljük meg frissíteni az adatbázist, amely CategoryID nem egyezik meg egy meglévő s CategoryIDkategóriával, a rendszer idegenkulcs-korlátozási szabálysértést követ el, és kivétel lép fel. Ebben a példában az látható, hogy egy tranzakció használatakor az idegenkulcs-korlátozás megsértéséből eredő kivétel a korábbi érvényes CategoryID módosítások visszaállítását eredményezi. Ha azonban nem használ tranzakciót, a kezdeti kategóriák módosításai megmaradnak.

Először nyissa meg a Transactions.aspx lapot a BatchData mappában, és húzzon egy GridView-t az eszközkészletből a Tervezőbe. Állítsa be az ID-t Products-re, és intelligens címkéje segítségével kösse egy új, ProductsDataSource nevű ObjectDataSource-hoz. Konfigurálja az ObjectDataSource-t, hogy lekérje az adatait az ProductsBLL osztály GetProducts metódusából. Ez egy írásvédett GridView lesz, ezért állítsa a legördülő listákat a(z) UPDATE, INSERT, és DELETE lapokon (Nincs) értékre, és kattintson a Befejezés gombra.

Az ObjectDataSource konfigurálása a ProductsBLL osztály GetProducts metódusának használatára

5. ábra: Az ObjectDataSource konfigurálása az ProductsBLL osztály GetProducts metódusának használatára (ide kattintva megtekintheti a teljes méretű képet)

Állítsa a Drop-Down listákat az UPDATE, INSERT és DELETE tabulátorokban a (Nincs) értékre

6. ábra: Állítsa a Drop-Down listákat az UPDATE, INSERT és DELETE tabulátorokban a (Nincs) értékre (ide kattintva megtekintheti a teljes méretű képet)

Az Adatforrás konfigurálása varázsló befejezése után a Visual Studio létrehozza a BoundFields és a CheckBoxField mezőket a termékadatmezőkhöz. Távolítsa el az összes mezőt, kivéve a ProductID, ProductName, CategoryID és CategoryName mezőket, és nevezze át a ProductName és CategoryName BoundFields HeaderText tulajdonságokat Termékre és Kategóriára. Az intelligens címkén jelölje be a Lapozás engedélyezése lehetőséget. A módosítások elvégzése után a GridView és az ObjectDataSource deklaratív korrektúrája a következőképpen fog kinézni:

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

Ezután vegyen fel három gombwebvezérlőt a GridView fölé. Állítsa az első Gomb szöveg tulajdonságát a Refresh Grid értékre, a másodikat pedig a Kategóriák módosítása (TRANZAKCIÓ) értékre, a harmadikat pedig a Kategóriák módosítása (TRANZAKCIÓ NÉLKÜL) értékre.

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

Ezen a ponton a Visual Studio Tervező nézetének a 7. ábrán látható képernyőfelvételhez hasonlóan kell kinéznie.

A lap GridView- és háromgombos webvezérlőket tartalmaz

7. ábra: A lap GridView-t és három gombos webvezérlőt tartalmaz (ide kattintva megtekintheti a teljes méretű képet)

Hozzon létre eseménykezelőket a három Button-esemény Click mindegyikéhez, és használja a következő kódot:

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

A Frissítés gomb eseménykezelője Click egyszerűen újrakonfigurálja az adatokat a GridView-hoz a Products GridView metódus meghívásával DataBind .

A második eseménykezelő újra hozzárendeli a termékeket CategoryID , és a BLL új tranzakciós metódusával hajtja végre az adatbázis-frissítéseket egy tranzakció alatt. Vegye figyelembe, hogy minden termék s CategoryID tetszőlegesen ugyanarra az értékre van beállítva, mint annak ProductID. Ez jól fog működni az első néhány termék esetében, mivel ezek a termékek olyan értékekkel rendelkeznek ProductID , amelyek történetesen érvényes CategoryID s-hez vannak megfeleltetve. De amikor az ProductID-k kezdenek túl nagyok lenni, ez a véletlenszerű átfedés az ProductID-k és CategoryID-k között már nem érvényes.

A harmadik Click eseménykezelő ugyanígy frissíti a termékeketCategoryID, de az alapértelmezett ProductsTableAdapter módszerrel elküldi a frissítést az Update adatbázisnak. Ez a Update metódus nem csomagolja be a parancsok sorozatát egy tranzakción belül, ezért ezek a módosítások az első észlelt idegenkulcs-korlátozás-megsértési hiba előtt is megmaradnak.

Ennek a viselkedésnek a bemutatásához látogasson el erre a lapra egy böngészőben. Kezdetben az adatok első oldalának kell megjelennie a 8. ábrán látható módon. Ezután kattintson a Kategóriák módosítása (TRANZAKCIÓVAL) gombra. Ez újbóli küldést okoz, és megkísérli frissíteni az összes termék CategoryID értékét, de ez idegenkulcs-korlátozás megsértését eredményezi (lásd a 9. ábrát).

A termékek egy lapozható GridView-ban jelennek meg

8. ábra: A termékek egy lapozható GridView-ban jelennek meg (ide kattintva megtekintheti a teljes méretű képet)

A kategóriák újbóli hozzárendelése idegenkulcs-korlátozás megsértését eredményezi

9. ábra: A kategóriák újbóli hozzárendelése idegenkulcs-korlátozási szabálysértést eredményez (kattintson ide a teljes méretű kép megtekintéséhez)

Most nyomja le a böngésző Vissza gombját, majd kattintson a Rács frissítése gombra. Az adatok frissítésekor pontosan ugyanazt a kimenetet kell látnia, mint a 8. ábrán. Ez azt jelenti, hogy annak ellenére, hogy egyes termékek CategoryID jogi értékekre változtak, és frissültek az adatbázisban, a külső kulcsok korlátozásának megsértésekor vissza lettek állítva.

Most kattintson a Kategóriák módosítása (TRANZAKCIÓ NÉLKÜL) gombra. Ez ugyanazt az idegenkulcs-korlátozásmegsértési hibát eredményezi (lásd a 9. ábrát), de ezúttal azokat a termékeket, amelyek CategoryID értékeit jogi értékre módosították, nem lesznek visszaállítva. Nyomja le a böngésző Vissza gombját, majd a Rács frissítése gombot. A 10. ábrán látható, hogy az első nyolc termék sajátosságait újra hozzárendelték. Például a 8. ábrán Chang-nál 1 volt CategoryID, de a 10. ábrán át lett rendelve 2-re.

Egyes termékek categoryID-értékei frissültek, míg mások nem

10. ábra: Egyes termékek CategoryID értékei frissültek, míg mások nem voltak (kattintson ide a teljes méretű kép megtekintéséhez)

Összefoglalás

Alapértelmezés szerint a TableAdapter metódusai nem burkolják a végrehajtott adatbázis-utasításokat egy tranzakció hatókörébe, de egy kis munkával olyan metódusokat adhatunk hozzá, amelyek létrehoznak, véglegesítenek és visszaállítanak egy tranzakciót. Ebben az oktatóanyagban három ilyen metódust hoztunk létre az ProductsTableAdapter osztályban: BeginTransaction, CommitTransactionés RollbackTransaction. Láttuk, hogyan használhatja ezeket a módszereket, valamint egy blokkot Try...Catch , hogy adatmódosítási utasítások sorozatát atomivá tegye. Létrehoztuk a UpdateWithTransaction metódust a ProductsTableAdapter-ben, amely a Batch Update mintát használva végrehajtja a megadott ProductsDataTable sorok szükséges módosításait. Hozzáadtuk a metódust a DeleteProductsWithTransactionProductsBLL BLL osztályához is, amely bemenetként elfogad egy ListProductID értéket, és meghívja az egyes DeleteDB-Direct mintametódusokatProductID. Mindkét módszer először létrehoz egy tranzakciót, majd végrehajtja az adatmódosítási utasításokat egy blokkon Try...Catch belül. Kivétel esetén a tranzakció visszaáll, különben véglegesítésre kerül.

Az 5. lépés a tranzakciós kötegfrissítések és a tranzakció használatát mellőző kötegfrissítések hatását szemlélteti. A következő három oktatóanyagban az oktatóanyagban lefektetett alapra építünk, és felhasználói felületeket hozunk létre a kötegfrissítések, törlések és beszúrások végrehajtásához.

Boldog programozást!

További olvasás

Az oktatóanyagban tárgyalt témakörökről az alábbi forrásokban talál további információt:

Tudnivalók a szerzőről

Scott Mitchell, hét ASP/ASP.NET-könyv szerzője és a 4GuysFromRolla.com alapítója, 1998 óta dolgozik a Microsoft webtechnológiáival. Scott független tanácsadóként, edzőként és íróként dolgozik. Legújabb könyve Sams Tanuld meg ASP.NET 2.0 24 óra alatt. Ő itt elérhető mitchell@4GuysFromRolla.com.

Külön köszönet

Ezt az oktatóanyag-sorozatot sok hasznos véleményező áttekintette. Az oktatóanyag fő véleményezői Dave Gardner, Hilton Giesenow és Teresa Murphy voltak. Szeretné áttekinteni a közelgő MSDN-cikkeimet? Ha igen, írj egy sort a mitchell@4GuysFromRolla.com-ra.