將資料庫修改包裝在交易中 (VB)

作者:Scott Mitchell

下載 PDF

本教學課程是四個查看更新、刪除和插入數據批次的第一個。 在本教學課程中,我們將了解資料庫交易如何允許批次修改作為不可部分完成的作業,以確保所有步驟都成功或所有步驟都失敗。

簡介

如我們所見,從 插入、更新和刪除數據 概觀開始教學課程,GridView 提供數據列層級編輯和刪除的內建支援。 只要按滑鼠即可建立豐富的數據修改介面,而不需要撰寫一行程式代碼,只要您是具有逐一數據列編輯和刪除的內容即可。 不過,在某些情況下,這不足,我們需要讓用戶能夠編輯或刪除一批記錄。

例如,大部分網頁式電子郵件用戶端會使用方格來列出每個郵件,其中每個數據列都包含複選框,以及電子郵件資訊 (主旨、寄件者等等) 。 此介面可讓使用者藉由檢查多個訊息,然後按兩下 [刪除選取的郵件] 按鈕來刪除多個訊息。 在使用者通常會編輯許多不同的記錄的情況下,批次編輯介面很理想。 相較於強制使用者按兩下 [編輯]、進行變更,然後針對需要修改的每個記錄單擊 [更新],批次編輯介面會轉譯每個數據列及其編輯介面。 使用者可以快速修改需要變更的數據列集,然後按下 [全部更新] 按鈕來儲存這些變更。 在本組教學課程中,我們將探討如何建立介面來插入、編輯和刪除數據批次。

執行批次作業時,請務必判斷批次中的某些作業是否應該成功,而其他作業失敗。 請考慮批次刪除介面 - 如果成功刪除第一筆選取的記錄,但第二筆記錄因為外鍵條件約束違規而失敗,會發生什麼事? 應該復原第一筆記錄的刪除,或第一筆記錄是否可繼續刪除?

如果您想要將批次作業視為 不可部分完成的作業,其中所有步驟都成功或所有步驟都失敗,則必須增強數據存取層,才能包含 資料庫交易的支援。 資料庫交易保證在交易的保護下執行的、 UPDATEDELETE 語句的INSERT不可部分完成性,而且是大部分新式資料庫系統所支援的功能。

在本教學課程中,我們將探討如何擴充 DAL 以使用資料庫交易。 後續的教學課程會檢查實作網頁以進行批次插入、更新和刪除介面。 讓我們開始吧!

注意

修改批次交易中的數據時,不一定需要不可部分完成性。 在某些情況下,可能會接受某些數據修改成功,而相同批次中的其他修改失敗,例如從網頁式電子郵件客戶端刪除一組電子郵件時。 如果在刪除程式期間發生資料庫錯誤,則可能可以接受這些記錄的處理,而不會刪除錯誤。 在這種情況下,不需要修改 DAL 以支持資料庫交易。 不過,還有其他批次作業案例,其中不可部分完成性很重要。 當客戶將資金從一個銀行帳戶移至另一個銀行帳戶時,必須執行兩項作業:必須從第一個帳戶扣除資金,然後再新增至第二個帳戶。 雖然銀行可能不考慮第一個步驟成功,但第二個步驟失敗,但其客戶可能會令人放心。 我們鼓勵您完成本教學課程,並實作 DAL 的增強功能,以支持資料庫交易,即使您不打算在批次插入、更新和刪除介面中使用它們,我們將在下列三個教學課程中建置。

交易概觀

大部分的資料庫 都包含交易的支援,可讓多個資料庫命令分組成單一邏輯工作單位。 組成交易的資料庫命令保證為不可部分完成,這表示所有命令都會失敗或全部成功。

一般而言,交易是透過 SQL 語句使用下列模式來實作:

  1. 指出交易的開頭。
  2. 執行組成交易的 SQL 語句。
  3. 如果步驟 2 的其中一個語句發生錯誤,請復原交易。
  4. 如果步驟 2 中的所有語句都完成且沒有錯誤,請認可交易。

撰寫 SQL 腳本或建立預存程式時,或透過程式設計方式使用 ADO.NET 命名空間中的類別,即可手動輸入用來建立、認可和回復交易的System.Transactions SQL 語句。 在本教學課程中,我們只會使用 ADO.NET 來檢查管理交易。 在未來的教學課程中,我們將探討如何在數據存取層中使用預存程式,此時我們將探索 SQL 語句來建立、回復和認可交易。 同時,如需詳細資訊,請參閱管理 SQL Server 預存程式中的交易。

注意

TransactionScope命名空間中的 System.Transactions 類別可讓開發人員以程序設計方式包裝交易範圍內的一系列語句,並包含涉及多個來源的複雜交易支援,例如兩個不同的資料庫或甚至是異質類型的數據存放區,例如 Microsoft SQL Server 資料庫、Oracle 資料庫和 Web 服務。 我決定針對本教學課程使用 ADO.NET 交易,而不是 類別,TransactionScope因為 ADO.NET 對資料庫交易來說更具體,而且在許多情況下,資源密集程度遠低。 此外,在某些情況下, TransactionScope 類別會使用 Microsoft Distributed Transaction Coordinator (MSDTC) 。 MSDTC 周圍的設定、實作和效能問題,使其成為特製化和進階主題,超出這些教學課程的範圍。

在 ADO.NET 中使用 SqlClient 提供者時,交易會透過對 類別 s BeginTransaction 方法SqlConnection呼叫來起始,這會傳回 SqlTransaction 物件。 修改交易的數據修改語句會放在區塊內 try...catch 。 如果在 區塊的 try 語句中發生錯誤,則執行會傳輸至 catch 區塊,其中交易可以透過 SqlTransaction 物件 s Rollback 方法復原。 如果所有語句都成功完成,則區塊結尾try的物件SqlTransactionCommit方法呼叫會認可交易。 下列代碼段說明此模式。

' 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

根據預設,具型別數據集中的 TableAdapters 不會使用交易。 為了提供交易的支持,我們需要增強 TableAdapter 類別,以包含使用上述模式的其他方法,以在交易範圍內執行一系列的數據修改語句。 在步驟 2 中,我們將瞭解如何使用部分類別來新增這些方法。

步驟 1:建立使用批處理數據網頁

在開始探索如何增強 DAL 以支持資料庫交易之前,讓我們先花一點時間建立本教學課程所需的 ASP.NET 網頁,以及下列三個網頁。 首先,新增名為 BatchData 的新資料夾,然後新增下列 ASP.NET 頁面,讓每個頁面與 Site.master 主版頁面產生關聯。

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

新增 SqlDataSource-Related 教學課程的 ASP.NET 頁面

圖 1:新增 SqlDataSource-Related 教學課程的 ASP.NET 頁面

如同其他資料夾, Default.aspx 將使用 SectionLevelTutorialListing.ascx 使用者控件來列出其區段中的教學課程。 因此,請將此使用者控制件Default.aspx從 方案總管 拖曳至頁面的設計檢視,以將它新增至 。

將 SectionLevelTutorialListing.ascx 使用者控件新增至 Default.aspx

圖 2:將使用者控件新增 SectionLevelTutorialListing.ascxDefault.aspx (按兩下即可檢視完整大小的影像)

最後,將這四個頁面新增為檔案的專案 Web.sitemap 。 具體而言,在自定義網站地圖 <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>

更新 Web.sitemap之後,請花點時間透過瀏覽器檢視教學課程網站。 左側功能表現在包含使用批次數據教學課程的專案。

網站地圖現在包含使用批次數據教學課程的專案

圖 3:網站地圖現在包含使用批次數據教學課程的專案

步驟 2:更新數據存取層以支持資料庫交易

如我們在第一個教學課程中所述, 建立數據存取層,DAL 中的具型別數據集是由 DataTables 和 TableAdapters 所組成。 當 TableAdapters 提供從資料庫讀取數據到 DataTables 的功能時,DataTables 會保存數據,以使用對 DataTable 所做的變更來更新資料庫,依此類推。 回想一下,TableAdapters 提供兩種更新數據的模式,也就是我稱為 Batch Update 和 DB-Direct。 使用 Batch 更新模式時,TableAdapter 會傳遞 DataRows 的 DataSet、DataTable 或集合。 系統會列舉此數據,並針對每個插入、修改或刪除的數據列、 InsertCommandUpdateCommandDeleteCommand 執行。 使用 DB-Direct 模式時,TableAdapter 會改為傳遞插入、更新或刪除單一記錄所需的數據行值。 然後,DB Direct 模式方法會使用這些傳入的值來執行適當的 InsertCommandUpdateCommandDeleteCommand 語句。

不論使用的更新模式為何,TableAdapters 自動產生的方法都不會使用交易。 根據預設,TableAdapter 所執行的每個插入、更新或刪除都會視為單一離散作業。 例如,假設 BLL 中的某些程式代碼會使用 DB-Direct 模式,將十筆記錄插入資料庫中。 此程式代碼會呼叫 TableAdapter s Insert 方法十次。 如果前五個插入成功,但第六個插入會導致例外狀況,前五筆插入的記錄會保留在資料庫中。 同樣地,如果使用 Batch 更新模式來執行插入、更新和刪除至 DataTable 中插入、修改和刪除的數據列,如果前幾個修改成功,但稍後的修改發生錯誤,則已完成的先前修改會保留在資料庫中。

在某些情況下,我們想要確保一系列修改的不可部分完成性。 若要達成此目的,我們必須手動擴充 TableAdapter,方法是新增方法,以在交易的的保護下執行 InsertCommandUpdateCommandDeleteCommand 。 在 建立數據存取層 中,我們探討如何使用 部分類別 來擴充具型別數據集內 DataTable 的功能。 這項技術也可以與 TableAdapters 搭配使用。

具類型的數據集 Northwind.xsd 位於 App_Code 資料夾的 DAL 子資料夾中。 在 DAL 名為 TransactionSupport 的資料夾中建立子資料夾,並新增名為 (的新 ProductsTableAdapter.TransactionSupport.vb 類別檔案,請參閱圖 4) 。 此檔案會保存 的部分實作 ProductsTableAdapter ,其中包含使用交易執行數據修改的方法。

新增名為 TransactionSupport 的資料夾和名為 ProductsTableAdapter.TransactionSupport.vb 的類別檔案

圖 4:新增名為 TransactionSupport 的資料夾和名為 的類別檔案 ProductsTableAdapter.TransactionSupport.vb

在檔案中 ProductsTableAdapter.TransactionSupport.vb 輸入下列程式代碼:

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

Partial此處類別宣告中的 關鍵詞會向編譯程式指出,在內加入的成員要新增至 ProductsTableAdapter 命名空間中的 NorthwindTableAdapters 類別。 記下檔案 Imports System.Data.SqlClient 頂端的語句。 由於 TableAdapter 已設定為使用 SqlClient 提供者,因此它會在內部使用 SqlDataAdapter 物件向資料庫發出其命令。 因此,我們需要使用 SqlTransaction 類別來開始交易,然後認可交易或復原它。 如果您使用 Microsoft SQL Server 以外的數據存放區,則必須使用適當的提供者。

這些方法提供啟動、復原和認可交易所需的建置組塊。 它們會標示 Public為 ,讓它們可從 ProductsTableAdapter中的 、DAL 中的另一個類別,或從架構中的另一層使用,例如 BLL。 BeginTransaction 會視) 需要開啟 TableAdapter 的內部 SqlConnection (、開始交易並將它指派給 Transaction 屬性,並將交易附加至內部 SqlDataAdapter 物件 SqlCommandCommitTransactionRollbackTransaction 分別呼叫 Transaction 物件的 CommitRollback 方法,再關閉內部 Connection 物件。

步驟 3:新增方法以在交易的保護之下更新和刪除數據

完成這些方法之後,我們即可將方法新增至 ProductsDataTable 或 BLL,以在交易的底下執行一系列命令。 下列方法會使用 Batch Update 模式來更新使用交易的 ProductsDataTable 實例。 它會呼叫 方法來啟動交易, BeginTransaction 然後使用 Try...Catch 區塊發出數據修改語句。 如果呼叫 Adapter 物件 s Update 方法會導致例外狀況,則執行會傳送至 catch 將復原交易的區塊,並重新擲回例外狀況。 回想一下, Update 方法會藉由列舉提供 ProductsDataTable 的數據列並執行必要的 InsertCommandUpdateCommandDeleteCommand ,來實作 Batch Update 模式。 如果其中任何一個命令導致錯誤,則會回復交易,復原交易存留期期間所做的先前修改。 Update如果語句完成但未發生錯誤,交易就會完全認可。

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 中的ProductsTableAdapter.TransactionSupport.vb部分類別,ProductsTableAdapter將方法新增至 類別。 或者,這個方法可以新增至商業規則層的 ProductsBLL 類別,但有一些次要的語法變更。 也就是說,、 和 中的 關鍵詞Me必須取代Adapter為 (召回率,Adapter這是類型ProductsTableAdapter為) 的屬性ProductsBLLMe.BeginTransaction()名稱。Me.RollbackTransaction()Me.CommitTransaction()

方法 UpdateWithTransaction 會使用 Batch 更新模式,但一系列 DB-Direct 呼叫也可以在交易範圍內使用,如下列方法所示。 方法DeleteProductsWithTransaction接受 做為 型別 Integer的輸入 List(Of T) ,這是ProductID要刪除的 。 方法會透過呼叫 BeginTransaction 起始交易,然後在 區塊中Try逐一查看提供的清單,針對每個ProductID值呼叫 DB-Direct 模式Delete方法。 如果有任何呼叫 Delete 失敗,控制權會傳送至 Catch 交易回復所在的區塊,並重新擲回例外狀況。 如果所有呼叫 Delete 都成功,則會認可交易。 將這個方法新增至 ProductsBLL 類別。

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

將交易套用至多個 TableAdapters

本教學課程中所檢查的交易相關程式代碼允許將的 ProductsTableAdapter 多個語句視為不可部分完成的作業。 但是,如果需要以不可部分完成的方式執行對不同資料庫數據表的多個修改,該怎麼辦? 例如,刪除類別時,我們可能會先想要將其目前的產品重新指派給一些其他類別。 這兩個步驟會重新指派產品並刪除類別,應該以不可部分完成的作業的形式執行。 ProductsTableAdapter但 只包含修改Products數據表的方法,CategoriesTableAdapter而 只包含修改Categories數據表的方法。 那麼,交易如何同時包含 TableAdapters?

其中一個選項是將方法新增至 CategoriesTableAdapter 具名 DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) ,並讓該方法呼叫預存程式,以重新指派產品,並在預存程式內定義的交易範圍內刪除類別。 我們將在未來教學課程中探討如何在預存程式中開始、認可和復原交易。

另一個選項是在 DAL 中建立包含 方法的 DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) 協助程式類別。 這個方法會建立 和的 CategoriesTableAdapter 實例, ProductsTableAdapter 然後將這兩個 TableAdapters Connection 屬性設定為相同的 SqlConnection 實例。 此時,這兩個 TableAdapters 的其中一個都會使用 呼叫 BeginTransaction來起始交易。 TableAdapters 方法可用來重新指派產品及刪除類別,會在區塊中 Try...Catch 叫用,並視需要認可或回復交易。

步驟 4:將UpdateWithTransaction方法新增至商業規則層

在步驟 3 中,我們已在 DAL 中新增 UpdateWithTransaction 方法 ProductsTableAdapter 。 我們應該將對應的方法新增至 BLL。 雖然簡報層可以直接呼叫 DAL 來叫 UpdateWithTransaction 用 方法,但這些教學課程致力於定義分層架構,以隔離 DAL 與簡報層。 因此,這會讓我們繼續這種方法。

開啟類別 ProductsBLL 檔案,並新增名為 UpdateWithTransaction 的方法,直接呼叫對應的 DAL 方法。 中現在應該有兩個新的方法 ProductsBLLUpdateWithTransaction,您剛才新增的方法,以及 DeleteProductsWithTransaction步驟 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

注意

這些方法不包含 DataObjectMethodAttribute 指派給 類別中 ProductsBLL 大部分其他方法的屬性,因為我們會直接從 ASP.NET 頁程序代碼後置類別叫用這些方法。 回想一下, DataObjectMethodAttribute 用來標幟 ObjectDataSource s Configure Data Source 精靈中應該顯示的方法,以及哪些索引卷標底下 (SELECT、UPDATE、INSERT 或 DELETE) 。 由於 GridView 缺少批次編輯或刪除的任何內建支援,因此我們必須以程式設計方式叫用這些方法,而不是使用無程式碼的宣告式方法。

步驟 5:從呈現層以不可部分完成的方式更新資料庫數據

為了說明交易在更新一批記錄時的效果,讓我們建立一個使用者介面來列出 GridView 中的所有產品,並包含按鈕 Web 控件,在按兩下時,重新指派產品 CategoryID 值。 特別是,類別重新指派將會進行,讓前幾個產品獲派有效 CategoryID 值,而其他產品則是有目的地指派不存在 CategoryID 的值。 如果我們嘗試使用不符合現有類別的產品CategoryIDCategoryID來更新資料庫,就會發生外鍵條件約束違規,並引發例外狀況。 在此範例中看到的是,使用異動時,從外鍵條件約束違規引發的例外狀況會導致先前的有效 CategoryID 變更回復。 不過,不使用交易時,將會保留對初始類別的修改。

從開啟資料夾中的頁面BatchData開始,Transactions.aspx並將 GridView 從 [工具箱] 拖曳至 Designer。 將其設定為 ProductsID並從其智慧標記將它系結至名為 ProductsDataSource的新 ObjectDataSource。 將 ObjectDataSource 設定為從 ProductsBLL 類別 s GetProducts 方法提取其數據。 這會是只讀的 GridView,因此請將 UPDATE、INSERT 和 DELETE 索引標籤標的下拉式清單設定為 ([無]) ,然後按兩下 [完成]。

將 ObjectDataSource 設定為使用 ProductsBLL 類別 s GetProducts 方法

圖 5:將 ObjectDataSource 設定為使用 ProductsBLL 類別 s GetProducts 方法, (按兩下即可檢視完整大小的影像)

將 UPDATE、INSERT 和 DELETE 索引標籤中的 Drop-Down 清單 設定為 [無] ()

圖 6:將 UPDATE、INSERT 和 DELETE 索引標籤中的 Drop-Down 清單 設定為 ([無]) (按兩下即可檢視全大小影像)

完成 [設定數據源精靈] 之後,Visual Studio 會為產品數據欄位建立 BoundFields 和 CheckBoxField。 移除 、、 和 以外的ProductID所有字段,並將 和 CategoryName BoundFields HeaderText 屬性分別重新命名ProductName為 Product 和 CategoryName Category。 CategoryIDProductName 從智慧標記中,核取 [啟用分頁] 選項。 進行這些修改之後,GridView 和 ObjectDataSource 的宣告式標記看起來應該如下所示:

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

接下來,在 GridView 上方新增三個 Button Web 控件。 將第一個 Button s Text 屬性設定為 [重新整理方格],第二個設定為 [修改類別] (WITH TRANSACTION) ,第三個則設定為 [修改類別] ([沒有 TRANSACTION]) 。

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

此時,Visual Studio 中的 [設計] 檢視看起來應該類似圖 7 所示的螢幕快照。

頁面包含 GridView 和三個按鈕 Web 控件

圖 7:頁面包含 GridView 和三個按鈕 Web 控件, (按兩下即可檢視完整大小的影像)

針對三個 Button s Click 事件中的每個事件建立事件處理程式,並使用下列程式代碼:

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

重新整理 Button 事件處理程式 Click 只會藉由呼叫 Products GridView s DataBind 方法將數據重新繫結至 GridView。

第二個事件處理程式會重新指派產品 CategoryID ,並使用 BLL 的新交易方法,在交易的保護之下執行資料庫更新。 請注意,每個產品 CategoryID 都會任意設定為與其 ProductID相同的值。 這適用於前幾個產品,因為這些產品有 ProductID 對應至有效 CategoryID 值。 但是,ProductID一旦開始太大,就不再套用 s 和 CategoryID 的重疊ProductID

第三 Click 個事件處理程式會以相同的方式更新產品 CategoryID ,但會使用 ProductsTableAdapter 預設 Update 方法將更新傳送至資料庫。 這個 Update 方法不會包裝交易內的一系列命令,因此會在第一次遇到外鍵條件約束違規錯誤之前進行這些變更會持續發生。

若要示範此行為,請透過瀏覽器瀏覽此頁面。 一開始您應該會看到第一頁的數據,如圖 8 所示。 接下來,按兩下 [修改類別 (WITH TRANSACTION) ] 按鈕。 這會導致回傳並嘗試更新所有產品 CategoryID 值,但會導致外鍵條件約束違規 (請參閱圖 9) 。

產品會顯示在可分頁的 GridView 中

圖 8:產品會顯示在可分頁的 GridView 中, (按兩下即可檢視完整大小的影像)

重新指派類別會導致外鍵條件約束違規

圖 9:重新指派類別會導致外鍵條件約束違規, (按兩下即可檢視完整大小的影像)

現在,按瀏覽器的 [上一步] 按鈕,然後按下 [重新整理方格] 按鈕。 重新整理資料時,您應該會看到完全相同的輸出,如圖 8 所示。 也就是說,即使某些產品 CategoryID 已變更為法律值,並在資料庫中更新,但在發生外鍵條件約束違規時,它們也會回復。

現在,請嘗試按兩下 [修改類別] ([沒有 TRANSACTION) ] 按鈕。 這會導致相同的外鍵條件約束違規錯誤 (請參閱圖 9) ,但這次變更為法律值的產品 CategoryID 將不會回復。 按瀏覽器的 [上一頁] 按鈕,然後按兩下 [重新整理方格] 按鈕。 如圖 10 所示, CategoryID 已重新指派前八項產品的 。 例如,在圖 8 中,耙有 1 個 CategoryID ,但在圖 10 中,已重新指派給 2。

有些產品 CategoryID 值已更新,有些則未更新

圖 10:某些產品 CategoryID 值已更新,但有些產品值未 (按兩下即可檢視完整大小的影像)

摘要

根據預設,TableAdapter 的 方法不會將已執行的資料庫語句包裝在交易的範圍內,但我們可以新增一些方法,以建立、認可和復原交易。 在本教學課程中,我們在 類別中 ProductsTableAdapter 建立了三種這類方法: BeginTransactionCommitTransactionRollbackTransaction。 我們已瞭解如何使用這些方法以及 Try...Catch 區塊,讓一系列的數據修改語句不可部分完成。 特別是,我們在 中ProductsTableAdapter建立了 UpdateWithTransaction 方法,它會使用 Batch Update 模式,對所提供的 ProductsDataTable數據列執行必要的修改。 我們也將 DeleteProductsWithTransaction 方法新增至 ProductsBLL BLL 中的 類別,其接受 ProductIDList 值的 作為其輸入,並針對每個ProductID呼叫 DB-Direct 模式方法Delete。 這兩種方法都是從建立交易開始,然後在區塊內 Try...Catch 執行數據修改語句。 如果發生例外狀況,則會回復交易,否則會認可該交易。

步驟 5 說明交易批次更新與忽略使用交易的批次更新的影響。 在接下來的三個教學課程中,我們將以本教學課程中的基礎為基礎,並建立使用者介面來執行批次更新、刪除和插入。

快樂的程序設計!

深入閱讀

如需本教學課程中討論之主題的詳細資訊,請參閱下列資源:

關於作者

Scott Mitchell 是七份 ASP/ASP.NET 書籍的作者,以及 1998 年以來與 Microsoft Web 技術合作的 4GuysFromRolla.com 作者。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格來連線到 ,您可以在 找到http://ScottOnWriting.NET

特別感謝

本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的首席檢閱者是 Dave Gardner、Linux Giesenow 和 Teresa Murphy。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。