Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
által Scott Mitchell
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:
- Egy tranzakció kezdetét jelzi.
- Hajtsa végre a tranzakciót alkotó SQL-utasításokat.
- Ha a 2. lépés egyik utasításában hiba lép fel, visszaállítsa a tranzakciót.
- 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.Transactions
haszná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 SqlTransaction
Rollback
visszaállítható. Ha az összes utasítás sikeresen befejeződött, a blokk végén az SqlTransaction
objektum 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
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.
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.sitemap
utá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.
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
, UpdateCommand
vagy 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
, UpdateCommand
vagy 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
.
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
).Adapter
ProductsBLL
ProductsTableAdapter
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 CategoriesTableAdapter
Categories
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 ProductsBLL
kell lennie: UpdateWithTransaction
az imént hozzáadott, és DeleteProductsWithTransaction
a 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 CategoryID
kategó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.
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)
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.
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).
8. ábra: A termékek egy lapozható GridView-ban jelennek meg (ide kattintva megtekintheti a teljes méretű képet)
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.
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 DeleteProductsWithTransaction
ProductsBLL
BLL osztályához is, amely bemenetként elfogad egy List
ProductID
értéket, és meghívja az egyes Delete
DB-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:
-
Tranzakciók egyszerűen:
System.Transactions
- TransactionScope és DataAdapters
- Oracle Database-tranzakciók használata a .NET-ben
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.