Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här handledningen är den första av fyra som tittar på uppdatering, borttagning och infogning av datapartier. I den här självstudien får vi lära oss hur databastransaktioner tillåter att batchändringar utförs som en atomisk åtgärd, vilket säkerställer att alla steg lyckas eller att alla steg misslyckas.
Inledning
Som vi såg från och med självstudiekursen En översikt över infogning, uppdatering och borttagning av data , ger GridView inbyggt stöd för redigering och borttagning på radnivå. Med några få klick med musen är det möjligt att skapa ett gränssnitt för omfattande datamodifiering utan att skriva en kodrad, så länge du är nöjd med redigering och borttagning per rad. I vissa scenarier är detta dock otillräckligt och vi måste ge användarna möjlighet att redigera eller ta bort en batch med poster.
Till exempel använder de flesta webbaserade e-postklienter ett rutnät för att visa varje meddelande där varje rad innehåller en kryssruta tillsammans med e-postinformationen (ämne, avsändare och så vidare). Med det här gränssnittet kan användaren ta bort flera meddelanden genom att kontrollera dem och sedan klicka på knappen Ta bort markerade meddelanden. Ett batchredigeringsgränssnitt är idealiskt i situationer där användare ofta redigerar många olika poster. I stället för att tvinga användaren att klicka på Redigera, göra ändringar och sedan klicka på Uppdatera för varje post som behöver ändras, renderar ett batchredigeringsgränssnitt varje rad med dess redigeringsgränssnitt. Användaren kan snabbt ändra den uppsättning rader som behöver ändras och sedan spara ändringarna genom att klicka på knappen Uppdatera alla. I den här uppsättningen självstudier ska vi undersöka hur du skapar gränssnitt för infogning, redigering och borttagning av batchar med data.
När du utför batchåtgärder är det viktigt att avgöra om det ska vara möjligt för vissa av åtgärderna i batchen att lyckas medan andra misslyckas. Överväg ett gränssnitt för batchborttagning – vad ska hända om den första valda posten har tagits bort, men den andra misslyckas, till exempel på grund av en begränsningsöverträdelse för sekundärnyckel? Ska den första postens borttagning återställas eller är det acceptabelt att den första posten förblir borttagen?
Om du vill att batchåtgärden ska behandlas som en atomisk åtgärd, en åtgärd där antingen alla steg lyckas eller alla steg misslyckas, måste dataåtkomstlagret utökas för att inkludera stöd för databastransaktioner. Databastransaktioner garanterar atomicitet för uppsättningen med INSERT
, UPDATE
och DELETE
-instruktioner som körs under transaktionens paraply och är en funktion som stöds av de flesta moderna databassystem.
I den här självstudien ska vi titta på hur du utökar DAL för att använda databastransaktioner. Kommande självstudier kommer att undersöka implementeringen av webbsidor för batchinförande, uppdatering och borttagning av gränssnitt. Låt oss komma igång!
Anmärkning
När du ändrar data i en batchtransaktion behövs inte atomitet alltid. I vissa scenarier kan det vara acceptabelt att vissa dataändringar lyckas och andra i samma batch misslyckas, till exempel när du tar bort en uppsättning e-postmeddelanden från en webbaserad e-postklient. Om det uppstår ett databasfel halvvägs genom borttagningsprocessen är det förmodligen acceptabelt att de poster som bearbetas utan fel förblir borttagna. I sådana fall behöver DAL inte ändras för att stödja databastransaktioner. Det finns dock andra scenarier för batchåtgärder där atomitet är avgörande. När en kund flyttar sina medel från ett bankkonto till ett annat måste två åtgärder utföras: pengarna måste dras av från det första kontot och sedan läggas till det andra. Även om banken kanske inte har något emot att det första steget lyckas men det andra steget misslyckas, skulle dess kunder förståeligt nog bli upprörda. Jag rekommenderar att du går igenom den här självstudien och implementerar förbättringarna av DAL för att stödja databastransaktioner även om du inte planerar att använda dem i batchinfogning, uppdatering och borttagning av gränssnitt som vi kommer att skapa i följande tre självstudier.
En översikt över transaktioner
De flesta databaser har stöd för transaktioner, vilket gör att flera databaskommandon kan grupperas i en enda logisk arbetsenhet. Databaskommandona som utgör en transaktion är garanterade atomiska, vilket innebär att alla kommandon misslyckas eller att alla lyckas.
I allmänhet implementeras transaktioner via SQL-instruktioner med hjälp av följande mönster:
- Ange början av en transaktion.
- Utför de SQL-instruktioner som utgör transaktionen.
- Om det finns ett fel i något av uttalandena från steg 2, rullar du tillbaka transaktionen.
- Om alla instruktioner från steg 2 slutförs utan fel genomför du transaktionen.
DE SQL-instruktioner som används för att skapa, checka in och återställa transaktionen kan anges manuellt när du skriver SQL-skript eller skapar lagrade procedurer, eller via programmatiska metoder med hjälp av antingen ADO.NET eller klasserna i System.Transactions
namnområdet. I den här självstudien kommer vi bara att undersöka hanteringen av transaktioner med hjälp av ADO.NET. I en framtida självstudie kommer vi att titta på hur du använder lagrade procedurer i dataåtkomstlagret. Då utforskar vi SQL-uttrycken för att skapa, återställa och genomföra transaktioner. Under tiden kan du läsa Hantera transaktioner i SQL Server-lagrade procedurer för mer information.
Anmärkning
KlassenTransactionScope
i System.Transactions
namnområdet gör det möjligt för utvecklare att programmatiskt omsluta en serie instruktioner inom omfånget för en transaktion och innehåller stöd för komplexa transaktioner som omfattar flera källor, till exempel två olika databaser eller till och med heterogena typer av datalager, till exempel en Microsoft SQL Server-databas, en Oracle-databas och en webbtjänst. Jag har bestämt mig för att använda ADO.NET-transaktioner för den här självstudien istället för TransactionScope
klassen eftersom ADO.NET är mer specifik för databastransaktioner och i många fall är mycket mindre resursintensiv. Under vissa scenarier TransactionScope
använder klassen dessutom Microsoft Distributed Transaction Coordinator (MSDTC). Konfigurations-, implementerings- och prestandaproblemen kring MSDTC gör det till ett ganska specialiserat och avancerat ämne och ligger utanför omfånget för dessa självstudier.
När du arbetar med SqlClient-providern i ADO.NET initieras transaktioner via ett anrop till SqlConnection
metoden class sBeginTransaction
, som returnerar ett SqlTransaction
objekt. De datamodifieringsinstruktioner som utgör transaktionen placeras inom ett try...catch
block. Om ett fel inträffar i ett uttalande i try
-blocket överförs körningen till catch
-blocket där transaktionen kan rullas tillbaka via SqlTransaction
-objektets Rollback
-metod. Om alla instruktioner har slutförts genomför ett anrop till SqlTransaction
objektets Commit
-metod i slutet av try
blocket transaktionen. Följande kodfragment illustrerar det här mönstret.
' 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
Som standard använder TableAdapters i en Typed DataSet inte transaktioner. För att ge stöd för transaktioner måste vi utöka TableAdapter-klasserna för att inkludera ytterligare metoder som använder mönstret ovan för att utföra en serie datamodifieringsinstruktioner inom omfånget för en transaktion. I steg 2 får vi se hur du använder partiella klasser för att lägga till dessa metoder.
Steg 1: Skapa webbsidor för att arbeta med batchdata
Innan vi börjar utforska hur du utökar DAL för att stödja databastransaktioner ska vi först ta en stund att skapa de ASP.NET webbsidor som vi behöver för den här självstudien och de tre som följer. Börja med att lägga till en ny mapp med namnet BatchData
och lägg sedan till följande ASP.NET sidor och associera varje sida med Site.master
huvudsidan.
Default.aspx
Transactions.aspx
BatchUpdate.aspx
BatchDelete.aspx
BatchInsert.aspx
Bild 1: Lägg till ASP.NET-sidorna för SqlDataSource-Related-tutorials
Precis som med de andra mapparna kommer Default.aspx
använda SectionLevelTutorialListing.ascx
Användarkontroll för att lista handledningarna inom sitt avsnitt. Lägg därför till den här användarkontrollen genom att Default.aspx
dra den från Solution Explorer till sidans designvy.
Bild 2: Lägg till SectionLevelTutorialListing.ascx
användarkontrollen Default.aspx
i (Klicka om du vill visa en bild i full storlek)
Slutligen lägger du till dessa fyra sidor som poster i Web.sitemap
filen. Mer specifikt lägger du till följande markering efter anpassningen av webbplatsöversikten <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>
Efter att du har uppdaterat Web.sitemap
, ta en stund att besöka självstudiewebbplatsen via en webbläsare. Menyn till vänster innehåller nu element för att arbeta med data i batchformat i handledningar.
Bild 3: Sajtkartan innehåller nu poster för handledningarna om att arbeta med batchdata
Steg 2: Uppdatera dataåtkomstskiktet för att stödja databastransaktioner
Som vi diskuterade i den första handledningen, Skapa ett dataåtkomstlager, består den typerade datamängden i vår DAL av DataTables och TableAdapters. DataTables innehåller data medan TableAdapters tillhandahåller funktioner för att läsa data från databasen till DataTables, uppdatera databasen med ändringar som gjorts i DataTables och så vidare. Kom ihåg att TableAdapters tillhandahåller två mönster för uppdatering av data, som jag kallade Batch Update och DB-Direct. Med mönstret för batchuppdatering skickas en DataSet, DataTable eller en samling DataRows till TableAdapter. Dessa data räknas upp och för varje infogad rad körs InsertCommand
, för varje ändrad rad körs UpdateCommand
, eller för varje borttagen rad körs DeleteCommand
. Med mönstret DB-Direct skickas i stället värdena för de kolumner som behövs för att infoga, uppdatera eller ta bort en enskild post. Metoden DB Direct-mönster använder sedan de insända värdena för att köra lämplig InsertCommand
- , UpdateCommand
eller DeleteCommand
-instruktion.
Oavsett vilket uppdateringsmönster som används använder inte TableAdapters automatiskt genererade metoder transaktioner. Som standard behandlas varje infogning, uppdatering eller borttagning som utförs av TableAdapter som en enskild diskret åtgärd. Anta till exempel att mönstret DB-Direct används av någon kod i BLL för att infoga tio poster i databasen. Den här koden anropar metoden TableAdapter s Insert
tio gånger. Om de första fem infogningarna lyckas, men det sjätte resulterade i ett undantag, skulle de första fem infogade posterna finnas kvar i databasen. På samma sätt, om Batch Update-mönstret används för att utföra infogningar, uppdateringar och borttagningar till infogade, ändrade och borttagna rader i en DataTable, om de första flera ändringarna lyckades men ett senare påträffade ett fel, skulle de tidigare ändringarna som slutfördes finnas kvar i databasen.
I vissa scenarier vill vi säkerställa atomitet i en serie ändringar. För att åstadkomma detta måste vi manuellt utöka TableAdapter genom att lägga till nya metoder som kör InsertCommand
, UpdateCommand
och DeleteCommand
inom ramen för en transaktion. I Skapa ett dataåtkomstlager tittade vi på att använda partiella klasser för att utöka funktionerna i DataTables i den typerade datauppsättningen. Den här tekniken kan också användas med TableAdapters.
Den typerade datauppsättningen Northwind.xsd
App_Code
finns i mappens DAL
undermapp. Skapa en undermapp i DAL
mappen med namnet TransactionSupport
och lägg till en ny klassfil med namnet ProductsTableAdapter.TransactionSupport.vb
(se bild 4). Den här filen kommer att innehålla den partiella implementeringen av ProductsTableAdapter
, som inkluderar metoder för att utföra dataändringar med hjälp av en transaktion.
Bild 4: Lägg till en mapp med namnet TransactionSupport
och en klassfil med namnet ProductsTableAdapter.TransactionSupport.vb
Ange följande kod i ProductsTableAdapter.TransactionSupport.vb
filen:
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
Nyckelordet Partial
i klassdeklarationen här anger för kompilatorn att medlemmarna som läggs till i ska läggas till i ProductsTableAdapter
klassen i NorthwindTableAdapters
namnområdet. Observera instruktionen Imports System.Data.SqlClient
överst i filen. Eftersom TableAdapter har konfigurerats för att använda SqlClient-providern använder den internt ett SqlDataAdapter
-objekt för att utfärda sina kommandon till databasen. Därför måste vi använda SqlTransaction
-klassen för att starta transaktionen och sedan begå eller ångra den. Om du använder ett annat datalager än Microsoft SQL Server måste du använda lämplig provider.
Dessa metoder tillhandahåller de byggstenar som krävs för att starta, återställa och genomföra en transaktion. De är markerade Public
, vilket gör att de kan användas inifrån ProductsTableAdapter
, från en annan klass i DAL eller från ett annat lager i arkitekturen, till exempel BLL.
BeginTransaction
öppnar TableAdapterns interna SqlConnection
(om det behövs), påbörjar transaktionen och tilldelar transaktionen till egenskapen Transaction
, och kopplar transaktionen till de interna SqlDataAdapter
:s SqlCommand
-objekt.
CommitTransaction
och RollbackTransaction
anropar Transaction
objektets respektive Commit
och Rollback
metoder innan det interna Connection
objektet stängs.
Steg 3: Lägga till metoder för att uppdatera och ta bort data inom ramen för en transaktion
När de här metoderna är klara är vi redo att lägga till metoder i ProductsDataTable
eller BLL som utför en serie kommandon inom ramen för en transaktion. Följande metod använder Batch Update-mönstret för att uppdatera en ProductsDataTable
instans med hjälp av en transaktion. Den startar en transaktion genom att anropa BeginTransaction
metoden och använder sedan ett Try...Catch
block för att utfärda datamodifieringsinstruktionerna. Om anropet till Adapter
-objektets Update
-metod resulterar i ett undantag, överförs körningen till catch
-blocket där transaktionen återställs och undantaget kastas igen. Kom ihåg att Update
metoden implementerar Batch Update-mönstret genom att räkna upp raderna i de angivna ProductsDataTable
och utföra nödvändiga InsertCommand
, UpdateCommand
och DeleteCommand
s. Om något av dessa kommandon resulterar i ett fel återställs transaktionen och de tidigare ändringarna som gjorts under transaktionens livslängd ångras. Om instruktionen Update
slutförs utan fel genomförs transaktionen i sin helhet.
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
UpdateWithTransaction
Lägg till metoden i ProductsTableAdapter
klassen via den partiella klassen i ProductsTableAdapter.TransactionSupport.vb
. Alternativt kan den här metoden läggas till i Business Logic LayerProductsBLL
-klassen med några mindre syntaktiska ändringar. Det vill säga, nyckelordet Me
i Me.BeginTransaction()
, Me.CommitTransaction()
och Me.RollbackTransaction()
skulle behöva ersättas med Adapter
(kom ihåg att Adapter
är namnet på en egenskap i ProductsBLL
av typen ProductsTableAdapter
).
Metoden UpdateWithTransaction
använder batchuppdateringsmönstret, men en serie DB-Direct-anrop kan också användas inom omfånget för en transaktion, som följande metod visar. Metoden DeleteProductsWithTransaction
accepterar som indata en List(Of T)
av typen Integer
, som är de ProductID
som ska tas bort. Metoden initierar transaktionen via ett anrop till BeginTransaction
och itererar sedan i Try
blocket via den angivna listan som anropar metoden DB-Direct mönster Delete
för varje ProductID
värde. Om något av anropen till Delete
misslyckas, överförs kontrollen till blocket Catch
, där transaktionen rullas tillbaka och undantaget kastas igen. Om alla anrop till Delete
lyckas, blir transaktionen genomförd. Lägg till den här metoden i ProductsBLL
klassen.
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
Tillämpa transaktioner över flera TableAdapters
Med den transaktionsrelaterade kod som beskrivs i den här handledningen kan flera uttalanden mot ProductsTableAdapter
behandlas som en atomisk operation. Men vad händer om flera ändringar av olika databastabeller måste utföras atomiskt? När vi till exempel tar bort en kategori kanske vi först vill omtilldela dess aktuella produkter till någon annan kategori. De här två stegen att omtilldela produkterna och att ta bort kategorin ska utföras som en atomär operation. Men ProductsTableAdapter
inkluderar endast metoder för att ändra Products
-tabellen och CategoriesTableAdapter
inkluderar endast metoder för att ändra Categories
-tabellen. Så hur kan en transaktion omfatta båda TableAdapters?
Ett alternativ är att lägga till en metod i den CategoriesTableAdapter
namngivna DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
metoden och låta metoden anropa en lagrad procedur som både omtilldelar produkterna och tar bort kategorin inom omfånget för en transaktion som definierats i den lagrade proceduren. Vi ska titta på hur du påbörjar, genomför och återställer transaktioner i lagrade procedurer i en framtida självstudie.
Ett annat alternativ är att skapa en hjälpklass i DAL som innehåller DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
metoden. Den här metoden skulle skapa en instans av CategoriesTableAdapter
och ProductsTableAdapter
och sedan ange dessa två TableAdapters-egenskaper Connection
till samma SqlConnection
instans. Då skulle någon av de två TableAdapters initiera transaktionen med ett anrop till BeginTransaction
. TableAdapters-metoderna för att omtilldela produkterna och ta bort kategorin anropas i ett Try...Catch
block där transaktionen bekräftas eller ångras efter behov.
Steg 4: Lägga tillUpdateWithTransaction
metoden i affärslogiklagret
I steg 3 lade vi till en UpdateWithTransaction
metod till ProductsTableAdapter
i DAL. Vi bör lägga till en motsvarande metod i BLL-filen. Presentationsskiktet kan anropa DAL direkt för att anropa UpdateWithTransaction
-metoden, men de här självstudierna har strävat efter att definiera en lagerbaserad arkitektur som skyddar DAL från presentationsskiktet. Därför är det upp till oss att fortsätta med den här metoden.
ProductsBLL
Öppna klassfilen och lägg till en metod med namnet UpdateWithTransaction
som helt enkelt anropar till motsvarande DAL-metod. Det bör nu finnas två nya metoder i ProductsBLL
: UpdateWithTransaction
, som du precis har lagt till och DeleteProductsWithTransaction
, som lades till i steg 3.
Public Function UpdateWithTransaction _
(ByVal products As Northwind.ProductsDataTable) As Integer
Return Adapter.UpdateWithTransaction(products)
End Function
Public Sub DeleteProductsWithTransaction _
(ByVal productIDs As System.Collections.Generic.List(Of Integer))
' Start the transaction
Adapter.BeginTransaction()
Try
' Delete each product specified in the list
For Each productID As Integer In productIDs
Adapter.Delete(productID)
Next
' Commit the transaction
Adapter.CommitTransaction()
Catch
' There was an error - rollback the transaction
Adapter.RollbackTransaction()
Throw
End Try
End Sub
Anmärkning
Dessa metoder inkluderar inte attributet DataObjectMethodAttribute
som tilldelats de flesta andra metoder i ProductsBLL
-klassen eftersom vi anropar dessa metoder direkt från kod-bakom-klasserna på ASP.NET-sidorna. Kom ihåg att DataObjectMethodAttribute
används för att markera vilka metoder som ska visas i guiden Konfigurera datakälla i ObjectDataSource och under vilken flik (SELECT, UPDATE, INSERT eller DELETE). Eftersom GridView saknar inbyggt stöd för batchredigering eller borttagning måste vi anropa dessa metoder programmatiskt i stället för att använda den kodfria deklarativa metoden.
Steg 5: Atomiskt uppdatera databasdata från presentationslagret
För att illustrera effekten som transaktionen har när du uppdaterar en batch med poster ska vi skapa ett användargränssnitt som visar alla produkter i en GridView och innehåller en knappwebbkontroll som, när den klickas, omtilldelar produktvärdena CategoryID
. I synnerhet fortsätter kategoriomtilldelningen så att de första flera produkterna tilldelas ett giltigt CategoryID
värde medan andra avsiktligt tilldelas ett obefintligt CategoryID
värde. Om vi försöker uppdatera databasen, och en produkt vars CategoryID
inte matchar en befintlig kategoris CategoryID
, så överträds en begränsning för främmande nyckel och ett undantag utlöses. Vad vi kommer att se i det här exemplet är att när du använder en transaktion kommer undantaget från begränsningen för den utländska nyckeln att leda till att de tidigare giltiga CategoryID
ändringarna återställs. När du inte använder en transaktion kommer dock ändringarna av de inledande kategorierna att finnas kvar.
Öppna först sidan Transactions.aspx
i mappen BatchData
och dra sedan en GridView från verktygslådan till designern. Ange dess ID
till Products
och, från dess smarta tagg, binda den till en ny ObjectDataSource med namnet ProductsDataSource
. Konfigurera ObjectDataSource för att hämta dess data från ProductsBLL
klassens GetProducts
-metod. Det här kommer att vara en endast läsbar GridView, så ställ in listrutorna på flikarna UPDATE, INSERT och DELETE till (Ingen) och klicka på Slutför.
Bild 5: Konfigurera ObjectDataSource att använda ProductsBLL
klassens GetProducts
metod (Klicka om du vill visa en bild i full storlek)
Bild 6: Ange Drop-Down-listorna i flikarna UPDATE, INSERT och DELETE till (Ingen) (Klicka om du vill visa en bild i full storlek)
När du har slutfört guiden Konfigurera datakälla skapar Visual Studio BoundFields och ett CheckBoxField för produktdatafälten. Ta bort alla dessa fält förutom ProductID
, ProductName
, CategoryID
, och CategoryName
och byt namn på ProductName
och CategoryName
BoundFields HeaderText
-egenskaperna till Produkt respektive Kategori. Från den smarta taggen markerar du alternativet Aktivera sidindelning. När du har gjort dessa ändringar bör GridView och ObjectDataSources deklarativa markering se ut så här:
<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>
Lägg sedan till tre knappwebbkontroller ovanför GridView. Ange texten för den första knappens egenskap till Uppdatera nät, den andra till Ändra kategorier (MED TRANSAKTION) och den tredje till Ändra kategorier (UTAN TRANSAKTION).
<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>
Nu bör designvyn i Visual Studio se ut ungefär som skärmbilden som visas i bild 7.
Bild 7: Sidan innehåller en GridView- och treknappswebbkontroller (Klicka om du vill visa en bild i full storlek)
Skapa händelsehanterare för var och en av de tre button-händelserna Click
och använd följande kod:
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
Uppdateringsknappens Click
händelsehanterare återbinder helt enkelt data till GridView genom att anropa metoden GridViews Products
.
Den andra händelsehanteraren tilldelar produkterna på nytt CategoryID
och använder den nya transaktionsmetoden från BLL för att utföra databasuppdateringarna inom ramen för en transaktion. Observera att varje produkts CategoryID
godtyckligt satt till samma värde som dess ProductID
. Detta fungerar bra för de första produkterna, eftersom dessa produkter har ProductID
värden som råkar mappas till giltiga CategoryID
s. Men när ProductID
s börjar bli för stora gäller inte längre denna tillfällig överlappning av ProductID
s och CategoryID
s.
Den tredje Click
händelsehanteraren uppdaterar produkterna CategoryID
på samma sätt, men skickar uppdateringen till databasen med standardmetoden ProductsTableAdapter
s Update
. Den här Update
metoden omsluter inte serien med kommandon i en transaktion, så alla ändringar som görs innan det första felet för överträdelse av främmande nyckelbegränsningar kommer att kvarstå.
Om du vill visa det här beteendet går du till den här sidan via en webbläsare. Till en början bör du se den första sidan med data enligt bild 8. Klicka sedan på knappen Ändra kategorier (MED TRANSAKTION). Detta kommer att orsaka en postback och försöka uppdatera alla produktvärden CategoryID
, men resulterar i en överträdelse av begränsningar för sekundärnyckel (se bild 9).
Bild 8: Produkterna visas i ett sidbart rutnätsvy (klicka om du vill visa en bild i full storlek)
Bild 9: Omtilldelning av kategorierna resulterar i en begränsningsöverträdelse för främmande nyckel (klicka för att visa i full storlek)
Tryck på bakåtknappen i webbläsaren och klicka sedan på knappen Uppdatera rutnät. När du uppdaterar data bör du se exakt samma utdata som visas i bild 8. Det innebär att även om vissa av produkterna CategoryID
ändrades till juridiska värden och uppdaterades i databasen, återställdes de när begränsningsöverträdelsen för främmande nyckel inträffade.
Försök nu att klicka på knappen Ändra kategorier (UTAN TRANSAKTION). Detta resulterar i samma fel för överträdelse av utländsk nyckelbegränsning (se bild 9), men denna gång återställs inte de produkter vars CategoryID
-värden har ändrats till ett giltigt värde. Tryck på bakåtknappen i webbläsaren och sedan på knappen Uppdatera rutnät. Som bild 10 visar har CategoryID
de första åtta produkterna fått nya tilldelningar. I bild 8 hade Chang till exempel en CategoryID
av 1, men i bild 10 har den omtilldelats till 2.
Bild 10: Vissa produktvärden CategoryID
uppdaterades medan andra inte var det (klicka om du vill visa en bild i full storlek)
Sammanfattning
TableAdapter-metoderna omsluter som standard inte de körda databasinstruktionerna inom omfånget för en transaktion, men med lite arbete kan vi lägga till metoder som skapar, checkar in och återställer en transaktion. I den här självstudien skapade vi tre sådana metoder i ProductsTableAdapter
klassen: BeginTransaction
, CommitTransaction
och RollbackTransaction
. Vi såg hur du använder dessa metoder tillsammans med ett Try...Catch
block för att göra en serie datamodifieringsinstruktioner atomiska. I synnerhet skapade vi metoden UpdateWithTransaction
i ProductsTableAdapter
, som använder sig av Batch Update-mönstret för att utföra de nödvändiga ändringarna av raderna i en angiven ProductsDataTable
. Vi har också lagt till DeleteProductsWithTransaction
-metoden i ProductsBLL
-klassen i BLL, som accepterar en List
som består av ProductID
-värden som indata och anropar mönstermetoden DB-Direct Delete
för varje ProductID
. Båda metoderna börjar med att skapa en transaktion och sedan köra datamodifieringsinstruktionerna i ett Try...Catch
block. Om ett undantag inträffar återställs transaktionen, annars bekräftas den.
Steg 5 illustrerade effekten av transaktionsbatchuppdateringar jämfört med batchuppdateringar som försummade att använda en transaktion. I de kommande tre självstudierna kommer vi att bygga vidare på grunden i den här självstudien och skapa användargränssnitt för att utföra batchuppdateringar, borttagningar och infogningar.
Lycka till med programmerandet!
Ytterligare läsning
Mer information om de ämnen som beskrivs i den här självstudien finns i följande resurser:
-
Enkelt att göra transaktioner:
System.Transactions
- TransactionScope och DataAdapters
- Använda Oracle Database-transaktioner i .NET
Om författaren
Scott Mitchell, författare till sju ASP/ASP.NET-böcker och grundare av 4GuysFromRolla.com, har arbetat med Microsofts webbtekniker sedan 1998. Scott arbetar som oberoende konsult, tränare och författare. Hans senaste bok är Sams Teach Yourself ASP.NET 2.0 på 24 timmar. Han kan nås på mitchell@4GuysFromRolla.com.
Särskilt tack till
Den här självstudieserien granskades av många användbara granskare. Huvudgranskare för den här handledningen var Dave Gardner, Hilton Giesenow och Teresa Murphy. Vill du granska mina kommande MSDN-artiklar? Om så är fallet, hör av dig på mitchell@4GuysFromRolla.com.