將資料庫修改包裝在交易中 (C#)
本教學課程是四個查看更新、刪除和插入數據批次的第一個。 在本教學課程中,我們將了解資料庫交易如何允許批次修改作為不可部分完成的作業,以確保所有步驟都成功或所有步驟都失敗。
簡介
如我們所見,從 插入、更新和刪除數據 概觀開始教學課程,GridView 提供數據列層級編輯和刪除的內建支援。 只要按滑鼠即可建立豐富的數據修改介面,而不需要撰寫一行程式代碼,只要您是具有逐一數據列編輯和刪除的內容即可。 不過,在某些情況下,這不足,我們需要讓用戶能夠編輯或刪除一批記錄。
例如,大部分網頁式電子郵件用戶端會使用方格來列出每個郵件,其中每個數據列都包含複選框,以及電子郵件資訊 (主旨、寄件者等等) 。 此介面可讓使用者藉由檢查多個訊息,然後按兩下 [刪除選取的郵件] 按鈕來刪除多個訊息。 在使用者通常會編輯許多不同的記錄的情況下,批次編輯介面很理想。 相較於強制使用者按兩下 [編輯]、進行變更,然後針對需要修改的每個記錄單擊 [更新],批次編輯介面會轉譯每個數據列及其編輯介面。 使用者可以快速修改需要變更的數據列集,然後按下 [全部更新] 按鈕來儲存這些變更。 在本組教學課程中,我們將探討如何建立介面來插入、編輯和刪除數據批次。
執行批次作業時,請務必判斷批次中的某些作業是否應該成功,而其他作業失敗。 請考慮批次刪除介面 - 如果成功刪除第一筆選取的記錄,但第二筆記錄因為外鍵條件約束違規而失敗,會發生什麼事? 應該復原第一筆記錄的刪除,或第一筆記錄是否可繼續刪除?
如果您想要將批次作業視為 不可部分完成的作業,其中所有步驟都成功或所有步驟都失敗,則必須增強數據存取層,才能包含 資料庫交易的支援。 資料庫交易保證在交易的保護下執行的、 UPDATE
和 DELETE
語句的INSERT
不可部分完成性,而且是大部分新式資料庫系統所支援的功能。
在本教學課程中,我們將探討如何擴充 DAL 以使用資料庫交易。 後續的教學課程會檢查實作網頁以進行批次插入、更新和刪除介面。 讓我們開始吧!
注意
修改批次交易中的數據時,不一定需要不可部分完成性。 在某些情況下,可能會接受某些數據修改成功,而相同批次中的其他修改失敗,例如從網頁式電子郵件客戶端刪除一組電子郵件時。 如果在刪除程式期間發生資料庫錯誤,則可能可以接受這些記錄的處理,而不會刪除錯誤。 在這種情況下,不需要修改 DAL 以支持資料庫交易。 不過,還有其他批次作業案例,其中不可部分完成性很重要。 當客戶將資金從一個銀行帳戶移至另一個銀行帳戶時,必須執行兩項作業:必須從第一個帳戶扣除資金,然後再新增至第二個帳戶。 雖然銀行可能不考慮第一個步驟成功,但第二個步驟失敗,但其客戶可能會令人放心。 我們鼓勵您完成本教學課程,並實作 DAL 的增強功能,以支持資料庫交易,即使您不打算在批次插入、更新和刪除介面中使用它們,我們將在下列三個教學課程中建置。
交易概觀
大部分的資料庫 都包含交易的支援,可讓多個資料庫命令分組成單一邏輯工作單位。 組成交易的資料庫命令保證為不可部分完成,這表示所有命令都會失敗或全部成功。
一般而言,交易是透過 SQL 語句使用下列模式來實作:
- 指出交易的開頭。
- 執行組成交易的 SQL 語句。
- 如果步驟 2 的其中一個語句發生錯誤,請復原交易。
- 如果步驟 2 中的所有語句都完成且沒有錯誤,請認可交易。
撰寫 SQL 腳本或建立預存程式時,或透過程式設計方式使用命名空間中的 ADO.NET 類別,即可手動輸入用來建立、認可和回復交易的System.Transactions
SQL 語句。 在本教學課程中,我們只會使用 ADO.NET 來檢查管理交易。 在未來的教學課程中,我們將探討如何在數據存取層中使用預存程式,此時我們將探索 SQL 語句來建立、回復和認可交易。
注意
TransactionScope
命名空間中的 System.Transactions
類別可讓開發人員以程序設計方式包裝交易範圍內的一系列語句,並包含涉及多個來源的複雜交易支援,例如兩個不同的資料庫或甚至是異質類型的數據存放區,例如 Microsoft SQL Server 資料庫、Oracle 資料庫和 Web 服務。 我決定針對本教學課程使用 ADO.NET 交易,而不是 類別,TransactionScope
因為 ADO.NET 對資料庫交易而言更具體,而且在許多情況下,會比較不耗用資源。 此外,在某些情況下, TransactionScope
類別會使用 Microsoft Distributed Transaction Coordinator (MSDTC) 。 MSDTC 周圍的設定、實作和效能問題,使其成為特製化和進階主題,超出這些教學課程的範圍。
在 ADO.NET 中使用 SqlClient 提供者時,交易會透過呼叫 SqlConnection
類別 s BeginTransaction
方法起始,這會傳回 SqlTransaction
物件。 修改交易的數據修改語句會放在區塊內 try...catch
。 如果在 區塊的 try
語句中發生錯誤,則執行會傳輸至 catch
區塊,其中交易可以透過 SqlTransaction
物件 s Rollback
方法復原。 如果所有語句都成功完成,則區塊結尾try
的物件SqlTransaction
Commit
方法呼叫會認可交易。 下列代碼段說明此模式。 請參閱 維護資料庫與交易的一致性。
// Create the SqlTransaction object
SqlTransaction myTransaction = SqlConnectionObject.BeginTransaction();
try
{
/*
* ... Perform the database transaction�s data modification statements...
*/
// If we reach here, no errors, so commit the transaction
myTransaction.Commit();
}
catch
{
// If we reach here, there was an error, so rollback the transaction
myTransaction.Rollback();
throw;
}
根據預設,具型別數據集中的 TableAdapters 不會使用交易。 為了提供交易的支持,我們需要增強 TableAdapter 類別,以包含使用上述模式的其他方法,以在交易範圍內執行一系列的數據修改語句。 在步驟 2 中,我們將瞭解如何使用部分類別來新增這些方法。
步驟 1:建立使用批處理數據網頁
在開始探索如何增強 DAL 以支持資料庫交易之前,讓我們先花一點時間建立本教學課程所需的 ASP.NET 網頁,以及下列三個網頁。 首先,新增名為 BatchData
的新資料夾,然後新增下列 ASP.NET 頁面,讓每個頁面與 Site.master
主版頁面產生關聯。
Default.aspx
Transactions.aspx
BatchUpdate.aspx
BatchDelete.aspx
BatchInsert.aspx
圖 1:新增 SqlDataSource-Related 教學課程的 ASP.NET 頁面
如同其他資料夾, Default.aspx
將使用 SectionLevelTutorialListing.ascx
使用者控件來列出其區段中的教學課程。 因此,請將此使用者控制件Default.aspx
從 方案總管 拖曳至頁面的設計檢視,將它新增至 。
圖 2:將使用者控件新增 SectionLevelTutorialListing.ascx
至 Default.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 或集合。 系統會列舉此數據,並針對每個插入、修改或刪除的數據列、 InsertCommand
、 UpdateCommand
或 DeleteCommand
執行。 使用 DB-Direct 模式時,TableAdapter 會改為傳遞插入、更新或刪除單一記錄所需的數據行值。 然後,DB Direct 模式方法會使用這些傳入的值來執行適當的 InsertCommand
、 UpdateCommand
或 DeleteCommand
語句。
不論使用的更新模式為何,TableAdapters 自動產生的方法都不會使用交易。 根據預設,TableAdapter 所執行的每個插入、更新或刪除都會視為單一離散作業。 例如,假設 BLL 中的某些程式代碼會使用 DB-Direct 模式,將十筆記錄插入資料庫中。 此程式代碼會呼叫 TableAdapter s Insert
方法十次。 如果前五個插入成功,但第六個插入會導致例外狀況,前五筆插入的記錄會保留在資料庫中。 同樣地,如果使用 Batch 更新模式來執行插入、更新和刪除至 DataTable 中插入、修改和刪除的數據列,如果前幾個修改成功,但稍後的修改發生錯誤,則已完成的先前修改會保留在資料庫中。
在某些情況下,我們想要確保一系列修改的不可部分完成性。 若要達成此目的,我們必須手動擴充 TableAdapter,方法是新增方法,以在交易的的保護下執行 InsertCommand
、 UpdateCommand
和 DeleteCommand
。 在 建立數據存取層 中,我們探討如何使用 部分類別 來擴充具型別數據集內 DataTable 的功能。 這項技術也可以與 TableAdapters 搭配使用。
具類型的數據集 Northwind.xsd
位於 App_Code
資料夾的 DAL
子資料夾中。 在 DAL
名為 TransactionSupport
的資料夾中建立子資料夾,並新增名為 (的新 ProductsTableAdapter.TransactionSupport.cs
類別檔案,請參閱圖 4) 。 此檔案會保存 的部分實作 ProductsTableAdapter
,其中包含使用交易執行數據修改的方法。
圖 4:新增名為 TransactionSupport
的資料夾和名為 的類別檔案 ProductsTableAdapter.TransactionSupport.cs
在檔案中 ProductsTableAdapter.TransactionSupport.cs
輸入下列程式代碼:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace NorthwindTableAdapters
{
public partial class ProductsTableAdapter
{
private SqlTransaction _transaction;
private SqlTransaction Transaction
{
get
{
return this._transaction;
}
set
{
this._transaction = value;
}
}
public void BeginTransaction()
{
// Open the connection, if needed
if (this.Connection.State != ConnectionState.Open)
this.Connection.Open();
// Create the transaction and assign it to the Transaction property
this.Transaction = this.Connection.BeginTransaction();
// Attach the transaction to the Adapters
foreach (SqlCommand command in this.CommandCollection)
{
command.Transaction = this.Transaction;
}
this.Adapter.InsertCommand.Transaction = this.Transaction;
this.Adapter.UpdateCommand.Transaction = this.Transaction;
this.Adapter.DeleteCommand.Transaction = this.Transaction;
}
public void CommitTransaction()
{
// Commit the transaction
this.Transaction.Commit();
// Close the connection
this.Connection.Close();
}
public void RollbackTransaction()
{
// Rollback the transaction
this.Transaction.Rollback();
// Close the connection
this.Connection.Close();
}
}
}
partial
此處類別宣告中的 關鍵詞會向編譯程式指出新增的成員要加入ProductsTableAdapter
命名空間中的 NorthwindTableAdapters
類別。 請注意檔案 using System.Data.SqlClient
頂端的語句。 由於 TableAdapter 已設定為使用 SqlClient 提供者,因此它會在內部使用 SqlDataAdapter
物件向資料庫發出其命令。 因此,我們需要使用 SqlTransaction
類別來開始交易,然後加以認可或回復。 如果您使用 Microsoft SQL Server 以外的數據存放區,則必須使用適當的提供者。
這些方法提供啟動、復原和認可交易所需的建置組塊。 它們會標示 public
為 ,讓它們可從 、DAL 中的另一個類別,或從架構中的另一層使用 ProductsTableAdapter
,例如 BLL。 BeginTransaction
視) 需要開啟 TableAdapter 的內部 SqlConnection
(、開始交易並將它指派給 Transaction
屬性,並將交易附加至內部 SqlDataAdapter
物件 SqlCommand
。 CommitTransaction
和 RollbackTransaction
在關閉內部Connection
物件之前,分別呼叫 Transaction
物件的 Commit
和 Rollback
方法。
步驟 3:新增方法以更新和刪除交易的保護下的數據
完成這些方法之後,我們就可以將方法新增至 ProductsDataTable
或 BLL,以在交易的保護下執行一系列命令。 下列方法會使用 Batch Update 模式,使用交易來更新 ProductsDataTable
實例。 它會藉由呼叫 BeginTransaction
方法來啟動交易,然後使用 try...catch
區塊發出數據修改語句。 如果呼叫 Adapter
物件的方法 Update
會產生例外狀況,則執行會傳送至 catch
將回復交易的區塊,並重新擲回例外狀況。 回想一下, Update
方法會藉由列舉所提供的 ProductsDataTable
數據列並執行必要的 InsertCommand
、 UpdateCommand
和 DeleteCommand
,來實作 Batch Update 模式。 如果其中任一個命令產生錯誤,交易就會回復,復原交易在交易存留期期間所做的先前修改。 Update
如果語句完成但不發生錯誤,交易就會完全認可。
public int UpdateWithTransaction(Northwind.ProductsDataTable dataTable)
{
this.BeginTransaction();
try
{
// Perform the update on the DataTable
int returnValue = this.Adapter.Update(dataTable);
// If we reach here, no errors, so commit the transaction
this.CommitTransaction();
return returnValue;
}
catch
{
// If we reach here, there was an error, so rollback the transaction
this.RollbackTransaction();
throw;
}
}
透過 UpdateWithTransaction
中的ProductsTableAdapter.TransactionSupport.cs
部分類別,ProductsTableAdapter
將方法新增至 類別。 或者,這個方法可以新增至商業規則層的 ProductsBLL
類別,但有一些次要的語法變更。 也就是說,在、 和 中的this.BeginTransaction()
關鍵字必須以 (重新叫Adapter
用取代Adapter
,這是類型ProductsTableAdapter
為) 中屬性ProductsBLL
this.RollbackTransaction()
的名稱。 this.CommitTransaction()
方法 UpdateWithTransaction
會使用 Batch Update 模式,但一系列 DB-Direct 呼叫也可以在交易的範圍內使用,如下列方法所示。 方法DeleteProductsWithTransaction
接受 做為 型int
別 的輸入List<T>
,這是ProductID
要刪除的 。 方法會透過呼叫 BeginTransaction
來起始交易,然後在 區塊中try
逐一查看提供的清單,針對每個ProductID
值呼叫 DB-Direct 模式Delete
方法。 如果有任何呼叫 Delete
失敗,控制權會傳輸到 catch
交易回復所在的區塊,並重新擲回例外狀況。 如果所有呼叫 Delete
都成功,則會認可交易。 將這個方法新增至 ProductsBLL
類別。
public void DeleteProductsWithTransaction
(System.Collections.Generic.List<int> productIDs)
{
// Start the transaction
Adapter.BeginTransaction();
try
{
// Delete each product specified in the list
foreach (int productID in productIDs)
{
Adapter.Delete(productID);
}
// Commit the transaction
Adapter.CommitTransaction();
}
catch
{
// There was an error - rollback the transaction
Adapter.RollbackTransaction();
throw;
}
}
跨多個 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 中,我們已將 方法新增 UpdateWithTransaction
至 ProductsTableAdapter
DAL 中的 。 我們應該將對應的方法新增至 BLL。 雖然簡報層可以直接呼叫 DAL 來叫 UpdateWithTransaction
用 方法,但這些教學課程致力於定義分層架構,以隔離 DAL 與呈現層。 因此,我們要繼續這種方法。
ProductsBLL
開啟類別檔案,並新增名為 UpdateWithTransaction
的方法,只呼叫對應的 DAL 方法。 現在應該有兩個新的方法 ProductsBLL
: UpdateWithTransaction
,您剛才新增了 ,而 DeleteProductsWithTransaction
步驟 3 中新增了 。
public int UpdateWithTransaction(Northwind.ProductsDataTable products)
{
return Adapter.UpdateWithTransaction(products);
}
public void DeleteProductsWithTransaction
(System.Collections.Generic.List<int> productIDs)
{
// Start the transaction
Adapter.BeginTransaction();
try
{
// Delete each product specified in the list
foreach (int productID in productIDs)
Adapter.Delete(productID);
// Commit the transaction
Adapter.CommitTransaction();
}
catch
{
// There was an error - rollback the transaction
Adapter.RollbackTransaction();
throw;
}
}
注意
這些方法不包含 DataObjectMethodAttribute
指派給 類別中大部分其他方法 ProductsBLL
的屬性,因為我們會直接從 ASP.NET 頁程序代碼後置類別叫用這些方法。 回想一下, DataObjectMethodAttribute
用來標幟 ObjectDataSource s Configure Data Source 精靈中應該顯示的方法,以及 ([選取]、[更新]、[插入] 或 [DELETE] 索引卷標底下的哪些方法) 。 由於 GridView 缺少批次編輯或刪除的任何內建支援,因此我們必須以程式設計方式叫用這些方法,而不是使用無程式碼宣告式方法。
步驟 5:從呈現層以不可部分完成的方式更新資料庫數據
為了說明交易在更新一批記錄時所擁有的效果,讓我們建立一個使用者介面,以列出 GridView 中的所有產品,並包含按鈕 Web 控件,在按兩下時,重新指派產品 CategoryID
值。 特別是,類別重新指派將會進行,讓前幾個產品獲指派有效 CategoryID
值,而其他產品則是有目的地指派不存在 CategoryID
的值。 如果我們嘗試以不符合現有類別的產品CategoryID
CategoryID
更新資料庫,將會發生外鍵條件約束違規,並引發例外狀況。 在此範例中看到的是,使用交易時,從外鍵條件約束違規引發的例外狀況會導致先前的有效 CategoryID
變更回復。 不過,不使用交易時,將會保留對初始類別的修改。
從開啟資料夾中的頁面BatchData
開始,Transactions.aspx
並將 GridView 從 [工具箱] 拖曳至 Designer。 將其ID
Products
設定為 ,並從其智慧標記將它系結至名為 ProductsDataSource
的新 ObjectDataSource。 設定 ObjectDataSource 以從 ProductsBLL
類別 s GetProducts
方法提取其數據。 這會是只讀的 GridView,因此請將 UPDATE、INSERT 和 DELETE 索引標籤標的下拉式清單設定為 (None) ,然後按兩下 [完成]。
圖 5:圖 5:將 ObjectDataSource 設定為使用 ProductsBLL
類別 s GetProducts
方法 (按兩下以檢視完整大小的影像)
圖 6:將 UPDATE、INSERT 和 DELETE 索引標籤中的 Drop-Down 清單 設定為 ([無]) (按兩下即可檢視大小完整的映像)
完成 [設定數據源精靈] 之後,Visual Studio 會為產品數據欄位建立 BoundFields 和 CheckBoxField。 移除 、、 和 以外的ProductID
所有字段,並將 和 CategoryName
CategoryName
BoundFields HeaderText
屬性分別重新命名ProductName
為 Product 和 CategoryID
Category。 ProductName
從智慧標記中,核取 [啟用分頁] 選項。 進行這些修改之後,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 所示的螢幕快照。
圖 7:頁面包含 GridView 和三個按鈕 Web 控件, (按兩下即可檢視完整大小的影像)
為每個 Button s Click
事件建立事件處理程式,並使用下列程式代碼:
protected void RefreshGrid_Click(object sender, EventArgs e)
{
Products.DataBind();
}
protected void ModifyCategoriesWithTransaction_Click(object sender, EventArgs e)
{
// Get the set of products
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = productsAPI.GetProducts();
// Update each product's CategoryID
foreach (Northwind.ProductsRow product in products)
{
product.CategoryID = product.ProductID;
}
// Update the data using a transaction
productsAPI.UpdateWithTransaction(products);
// Refresh the Grid
Products.DataBind();
}
protected void ModifyCategoriesWithoutTransaction_Click(object sender, EventArgs e)
{
// Get the set of products
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = productsAPI.GetProducts();
// Update each product's CategoryID
foreach (Northwind.ProductsRow product in products)
{
product.CategoryID = product.ProductID;
}
// Update the data WITHOUT using a transaction
NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
new NorthwindTableAdapters.ProductsTableAdapter();
productsAdapter.Update(products);
// Refresh the Grid
Products.DataBind();
}
重新整理 Button 事件處理程式 Click
只會藉由呼叫 Products
GridView s DataBind
方法,將數據重新繫結至 GridView。
第二個事件處理程式會重新指派產品 CategoryID
,並使用 BLL 的新交易方法,在交易的的保護下執行資料庫更新。 請注意,每個產品 CategoryID
都會任意設定為與其 ProductID
相同的值。 這適用於前幾個產品,因為這些產品有 ProductID
對應至有效 CategoryID
s 的值。 但是,ProductID
一旦開始太大,就不再套用 s 和 CategoryID
s 的重ProductID
疊。
第三個 Click
事件處理程式會以相同的方式更新產品 CategoryID
,但使用 ProductsTableAdapter
預設 Update
方法將更新傳送至資料庫。 這個 Update
方法不會包裝交易內的一系列命令,因此會在第一次遇到外鍵條件約束違規錯誤之前進行這些變更會保存。
若要示範此行為,請透過瀏覽器瀏覽此頁面。 一開始您應該會看到第一頁的數據,如圖 8 所示。 接下來,按兩下 [修改類別 (WITH TRANSACTION) ] 按鈕。 這會導致回傳並嘗試更新所有產品 CategoryID
值,但會導致外鍵條件約束違規 (請參閱圖 9) 。
圖 8:產品會顯示在可分頁的 GridView 中, (按兩下即可檢視大小完整的影像)
圖 9:重新指派類別會導致外鍵條件約束違規, (按兩下即可檢視大小完整的影像)
現在,按瀏覽器的 [上一步] 按鈕,然後按下 [重新整理網格線] 按鈕。 重新整理資料時,您應該會看到完全相同的輸出,如圖 8 所示。 也就是說,即使某些產品 CategoryID
已變更為法律值,並在資料庫中更新,但在外鍵條件約束違規發生時,它們仍會回復。
現在,請嘗試按兩下 [修改類別] ([不使用 TRANSACTION) ] 按鈕。 這會導致相同的外鍵條件約束違規錯誤, (請參閱圖 9) ,但這次變更 CategoryID
為合法值的產品將不會回復。 按瀏覽器的 [上一步] 按鈕,然後按兩下 [重新整理網格線] 按鈕。 如圖 10 所示, CategoryID
前八個產品的 s 已重新指派。 例如,在圖 8 中,嫡嫯有 CategoryID
1 個,但在圖 10 中,已重新指派給 2。
圖 10:某些產品值已更新,而有些產品 CategoryID
值未 (按兩下即可檢視大小完整的影像)
摘要
根據預設,TableAdapter s 方法不會在交易範圍內包裝執行的資料庫語句,但有一些工作,我們可以新增方法來建立、認可和復原交易。 在本教學課程中,我們在 類別中 ProductsTableAdapter
建立了三個這類方法: BeginTransaction
、 CommitTransaction
和 RollbackTransaction
。 我們已瞭解如何使用這些方法以及 try...catch
區塊,讓一系列的數據修改語句不可部分完成。 特別是,我們在 中ProductsTableAdapter
建立了 UpdateWithTransaction
方法,它會使用 Batch Update 模式對提供 ProductsDataTable
的數據列執行必要的修改。 我們也將 DeleteProductsWithTransaction
方法新增至 ProductsBLL
BLL 中的 類別,它會接受 List
值的 ProductID
作為其輸入,並針對每個ProductID
呼叫 DB-Direct 模式方法Delete
。 這兩種方法都是從建立交易開始,然後在 區塊內 try...catch
執行數據修改語句。 如果發生例外狀況,則會回復交易,否則會認可該交易。
步驟 5 說明交易式批次更新與忽略使用交易的批次更新的影響。 在接下來的三個教學課程中,我們將根據本教學課程中的基礎來建置,並建立使用者介面來執行批次更新、刪除和插入。
快樂的程序設計!
深入閱讀
如需本教學課程中所討論之主題的詳細資訊,請參閱下列資源:
關於作者
Scott Mitchell 是 1998 年以來,1998 年與 Microsoft Web 技術合作的 七篇 ASP/ASP.NET 書籍和 4GuysFromRolla.com 作者。 Scott 是獨立的顧問、訓練者和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格連到,也可以透過其部落格來存取,網址為 http://ScottOnWriting.NET。
特別感謝
本教學課程系列是由許多實用的檢閱者所檢閱。 本教學課程的首席檢閱者是 Dave Gardner、一個區格 Giesenow 和 Teresa Murphy。 想要檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行放在 mitchell@4GuysFromRolla.com。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應