程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> ASP.NET >> ASP.NET基礎 >> 在ASP.NET 2.0中操作數據之六十一:在事務裡對數據庫修改進行封裝

在ASP.NET 2.0中操作數據之六十一:在事務裡對數據庫修改進行封裝

編輯:ASP.NET基礎

導言:

  正如我們在第16章《概述插入、更新和刪除數據》裡探討的那樣,GridView控件內建的功能支持對每行數據的編輯和刪除功能,你只需要稍稍動一下鼠標就可以創建豐富的數據修改界面而不用寫一行代碼.但是,在某些情況下,這還不夠,我們需要讓用戶能夠成批地處理數據.

  比如,很多基於web(web-based)的電子郵件客戶端,將所有郵件出來,每條郵件除了包含郵件信息(主題、發送者等)外,還包含一個checkbox控件。這些界面允許用戶同時刪除多個郵件,用戶只需要選中郵件,再點"刪除所選郵件"按鈕.當用戶要編輯多條不同的記錄的時候,提供一個批編輯界面是比較理想的.我們用不著讓用戶每次都選中一條要編輯的記錄,再做相關的修改,最後點“更新”按鈕,在批編輯界面裡每條記錄都有各自的編輯選項,用戶可以快速地編輯多條記錄再點“Update All”按鈕來保存對他們所做的修改.本系列我們將考察如何創建對數據進行添加、編輯、刪除批處理的界面.

  如果想對批處理執行atomic operation(原子操作), 那麼首先,所做的操作要麼都執行成功要麼都失敗,另外還要對數據訪問層進行擴充以支持database transactions(數據庫事務)。數據庫事務確保INSERT, UPDATE, 和 DELETE語句執行的atomicity(原子數)置於數據庫事務的保護之下.另外,絕大多數的當代數據庫系統都支持數據庫事務.

  在本系列我們先看如何擴充數據訪問層以支持數據庫事務,接下來我們看如何創建頁面以包含添加、更新、刪除數據的批處理界面,讓我們開始吧.

  注意:在批處理事務裡修改數據時,原子數(atomicity)並非總數必要的。在批處理的某些情況下,某些修改成功某些修改失敗是可以接受的。比如刪除電子郵件時,有些郵件在刪除過程中發生了數據庫錯誤,有些郵件沒有發生錯誤,對這種沒有發生錯誤的郵件,批處理照樣將其刪除掉.對這種情況,我們沒有必要設置數據訪問層DAL支持數據庫事務.不過在其它某些情況下,原子數是至關重要的.比如某個客戶想把資金從一個銀行帳戶轉移到另一個銀行帳號,下面2個操作必須執行成功:首先,將第一個帳號的資金扣除,然後將資金轉入第二個帳號.如果第一步執行成功,第二步執行失敗,銀行當然高興,客戶怕是要發瘋了.在後面的文章裡我們將創建添加、更新、刪除的批處理界面,就算你不打算在這些頁面裡使用數據庫事務,我也希望你照著本篇文章,對數據訪問層進行擴展一支持數據庫事務.

事務概述

絕大多數的數據庫都支持事務,它可以將多個數據庫命令當成一個邏輯單位進行處理.這些包含事務的命令要麼都執行成功要麼都執行失敗.

一般來說,事務通過SQL命令來執行,使用如下的模式:

1.聲明事務開始
2.執行構成事務的那些SQL命令
3.如果在第二步中的任何一個命令出錯,執行事務回滾(rollback the transaction)
4.如果在第二步中的所有命令成功執行,提交事務

  這些SQL命令可以通過手寫的方式輸入,比如寫SQL腳本、創建存儲過程、也可以通過編程的方式來構建,比如使用ADO.NET技術或調用System.Transactions namespace命名空間的類.在本文,我們僅僅考察用ADO.NET技術管理事務.在後面的教程我們看如何在數據訪問層Data Access Layer裡使用存儲過程,到那時,我們再來考察這些創建、回滾、提交事物的SQL命令。另外,要獲得更多信息請參考文章《Managing Transactions in SQL Server Stored Procedures》(http://www.4guysfromrolla.com/webtech/080305-1.shtml)

  注意:System.Transactions namespace命名空間的TransactionScope class類允許開發者通過編程的方式獲取事務裡的一系列命令,且允許事務包含多個數據源,甚至類型不同,比如:Microsoft SQL Server database, 或Oracle database,甚至Web service.本教程我們使用ADO.NET技術而非TransactionScope class類,是因為ADO.NET指定數據庫事務更詳細,且在很多情況下占用資源更少.此外,在某些情況下,TransactionScope class類要用到Microsoft Distributed Transaction Coordinator (MSDTC),圍繞MSDTC的配置、執行和性能問題是比較專業、高級的問題稍微超出了本教程的范圍.

  在ADO.NET裡,通過調用SqlConnection class類的BeginTransaction method方法啟動事務, 該方法返回一個SqlTransaction object對象.將構成事務的數據操作命令放在try...catch區域,如果在try區域的某個命令出錯的話,程序將轉到catch區域,在此,通過SqlTransaction object對象的Rollback method方法執行事務回滾。如果所有的命令執行成功,將調用位於try區域底部的SqlTransaction object對象的Commit method方法來提交事務.下面的代碼片段揭示了該模式。要想看在ADO.NET裡使用事務的更多例子,請參閱文章《Maintaining Database Consistency with Transactions》(http://aspnet.4guysfromrolla.com/articles/072705-1.aspx).

// 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;
}

  默認情況下,強類型數據集(Typed DataSet)裡的TableAdapters並不使用事務。為此,我們要對TableAdapter classes類進行擴展,以包含額外的方法以使用上述模式來執行事務。在第二步,我們看如何使用一個partial classes類來添加這些方法.

第一步:創建批處理數據的頁面

  在我們考察如何擴展數據訪問層DAL以支持數據庫事務之前,讓我們花點時間來創建一些ASP.NET web頁面,我們在本章及後面三章將用到它們.

添加一個名為BatchData的新文件夾,再添加如下的 ASP.NET頁面, 務必套用Site.master模板頁.

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

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401168.gif
圖1:添加相關的頁面

就像其它文件夾裡的Default.aspx頁面一樣,用SectionLevelTutorialListing.ascx用戶控件來列出本部分的章節。將其從解決資源管理器裡拖到Default.aspx頁面.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401255.gif
圖2:將SectionLevelTutorialListing.ascx用戶控件添加到Default.aspx頁面

最後添加如下代碼到Web.sitemap文件,具體的,將其添加到“Customizing the Site Map” <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>

完成後,花幾分鐘在浏覽器裡登錄頁面,左面的菜單列出了本部分的各項

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401203.gif
圖3:Site Map現在包含了本章節

第二步:更新數據訪問層以支持數據庫事務

  就像我們在第一章《創建一個數據訪問層》探討的一樣,位於數據訪問層的強類型數據集(Typed DataSet)由DataTables 和 TableAdapters構成.  DataTables保存數據,而TableAdapters提供相應的方法從數據庫讀取數據,並根據DataTables的改動對數據庫做相應的更新,等等.記得TableAdapters有2種更新數據的模式——Batch Update 和 DB-Direct.就Batch Update模式而言, TableAdapter可以傳入DataSet, DataTable, 或DataRows集,遍歷這些數據對要添加、修改、刪除的行執行相應的InsertCommand, UpdateCommand, or DeleteCommand方法。就DB-Direct模式而言,TableAdapter傳入的是那些需要進行添加、更新、刪除操作的某條記錄的列的值,再使用這些值執行相關的InsertCommand, UpdateCommand, 或DeleteCommand命令.

  TableAdapter自動生成的方法並不使用事務.默認狀態下,TableAdapter執行的每一個insert, update, 或delete操作都看作是單獨的、互不相干的.假定在業務邏輯層BLL裡使用DB-Direct模式來向數據庫添加十條記錄,代碼將分十次調用TableAdapter的Insert方法. 如果前5條記錄添加正常,而在添加第六條記錄時發生異常,前5條記錄仍然保存在數據庫.同樣的,用Batch Update模式來操作的話,效果亦然.

  在某些情況下,我們想確保在進行一系列的改動時引入原子數(atomicity).為此,我們必須手動擴展TableAdapter,通過添加一些新的方法將InsertCommand, UpdateCommand, 和DeleteCommands命令置於事務之下.在第一章《創建一個數據訪問層》裡,我們考察了使用部分類(partial classes)對強類型數據集(Typed DataSet)裡的DataTable的函數進行擴充.該技術同樣適用於TableAdapter.

  強類型數據集Northwind.xsd位於App_Code文件夾的DAL子文件夾裡.在DAL文件夾裡再創建一個名為TransactionSupport的子文件夾,再在裡面添加一個新類,名為ProductsTableAdapter.TransactionSupport.cs (見圖4).該類包含ProductsTableAdapter的使用事務的方法.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401207.gif
圖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向編譯器表明代碼裡添加的成員(members)是添加到命名空間NorthwindTableAdapters裡的ProductsTableAdapter class類.我們注意到在文件的頂部有一個using System.Data.SqlClient聲明,這是因為TableAdapter被設置為使用SqlClient provider,在其內部使用一個SqlDataAdapter object對象來向數據庫發出命令.因此,我們需要使用SqlTransaction class類來啟動事務,然後提交或回滾事務.如果沒有使用Microsoft SQL Server數據庫的話,你需要調用恰當的provider.

  這些方法被標記為public,我們可以在ProductsTableAdapter裡,或數據訪問層DAL的其它類,甚至是其它層比如業務邏輯層BLL來調用這些法.
BeginTransaction()方法打開了TableAdapter的內部的SqlConnection(如果需要的話), 開啟事務並賦值給Transaction屬性,並將事務分配(attache)給SqlDataAdapter的SqlCommand objects對象.CommitTransaction()和 RollbackTransaction()方法在關閉內部的Connection object對象前分別調用Transaction object對象的Commit 和 Rollback方法.

  添加上述代碼後,我們將在ProductsDataTable 或業務邏輯層BLL裡添加方法以執行一系列的置於事務之下的命令. 下面的代碼在Batch Update pattern模式裡使用一個事務來更新一個ProductsDataTable instance實例.它調用BeginTransaction method方法來啟動一個事務,然後用一個try...catch模塊來發布數據更改命令.如果調用Adapter object對象的Update方法出現異常,那麼將轉到catch區域,對事務進行回滾.記得執行Batch Update pattern模式的Update方法將遍歷ProductsDataTable裡的所有行(rows),執行相應的InsertCommand, UpdateCommand, 和DeleteCommands命令.如果這些命令中的其中一個出現異常,事務將回滾,撤銷在事務裡的所做的更改.如果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 class類。另外,還可以將該方法添加到業務邏輯層的ProductsBLL class類,不過要做些許修改:即將this.BeginTransaction(), this.CommitTransaction(), and this.RollbackTransaction()三中方法裡的關鍵字“this”替換為“Adapter”(我們知道,ProductsBLL類裡的ProductsTableAdapter的name屬性即是Adapter).

  UpdateWithTransaction()方法使用的是Batch Update模式,不過也可在事務裡調用DB-Direct模式,就像下面的代碼顯示的那樣.DeleteProductsWithTransaction()方法接受一個int類型的List<T>,也就是要刪除的ProductIDs.該方法通過調用BeginTransaction來啟動事務,然後在try模塊裡對每一個ProductID值調用DB-Direct模式的Delete方法.如果任何一個對Delete的調用出錯,將轉到catch 模塊,事務將會回滾;如果所有對Delete的調用成功,那就提交事務。添加該方法給ProductsBLL class類.

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裡的多個命令采用原子操作.如果我們是對多個不同的數據庫表進行改動,並對這些改動執行原子操作那又怎麼辦呢?比如:當刪除一個category時,在刪除之前我們想把該種類對應的products分配給其它的category.對這種2步操作——分配products和刪除category——應該執行原子操作.但是ProductsTableAdapter只包含修改Products表的方法;而CategoriesTableAdapter只包含修改Categories表的方法.那麼怎樣使用一個包含這2個TableAdapters的事務呢?

  其中一個辦法是向CategoriesTableAdapter添加一個名為DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)的方法.再定義一個方法來調用一個存儲過程,使用事務來達到分配products和刪除category的目的.我們將在後面考察在一個存儲過程裡開始、提交和回滾事務.

  另一個方法是在數據訪問層裡添加一個類,來包含DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)方法.該方法創建CategoriesTableAdapter 和 the ProductsTableAdapter的實例,並將這2個TableAdapters的Connection屬性設置為相同的SqlConnection實例。這樣,它們都將調用BeginTransaction來開啟事務.然後在try...catch模塊裡執行分配products和刪除category的方法,最後提交或回滾事務.

第四步:向業務邏輯層添加UpdateWithTransaction方法

  在第三步我們向數據訪問層DAL裡的ProductsTableAdapter添加了一個UpdateWithTransaction方法,我們將向業務邏輯層添加相應的方法.雖然表現層可以直接向DAL調用UpdateWithTransaction方法,但是我們在這裡仍然將它們分隔開。

  打開ProductsBLL class類,添加一個名為UpdateWithTransaction的方法,該方法僅僅簡單地調用對應的DAL方法.現在ProductsBLL類裡有2個方法:UpdateWithTransaction方法——我們才添加的;以及DeleteProductsWithTransaction——我們在第三步添加的.

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

  注意:根ProductsBLL類裡的大部分方法不同,上述方法並不包含DataObjectMethodAttribute屬性。這是因為我們將直接在ASP.NET頁面的後台代碼裡調用這些方法,記得DataObjectMethodAttribute方法的作用是指出哪些方法應該出現在ObjectDataSource控件的設置數據源向導的某些標簽(SELECT, UPDATE, INSERT, 或DELETE)裡.由於GridView控件缺乏內置的支持“批編輯”或“批刪除”的功能,我們將通過編輯的方式來調用這些方法.

第五步:在表現層更新數據庫數據

  為演示更新一批記錄時事務的作用,我們將創建一個用戶界面來將所有產品用一個GridView控件顯示出來,並包含一個Button Web控件。當點擊該按鈕時為product重新賦值一個有效的CategoryID值。具體來說,對頭幾個products分配一個有效的CategoryID值;而剩下的分配一個無效的(non-existent)CategoryID值,當我們試圖對這樣的一個product——其CategoryID值與現有的category的CategoryID不匹配——進行更新時,將違反外鍵約束,進而拋出一個異常.在本文的示例裡你將看到,在使用事務時,當違反外鍵約束拋出一個異常時將導致前面的正確分配CategoryID值的操作產生回滾.如果不使用事務的話,這些正確的操作將執行成功.

  首先,打開BatchData文件夾裡的Transactions.aspx頁面,從工具箱拖一個GridView控件到頁面。設置其ID為Products,從其智能標簽裡將其綁定到一個名為ProductsDataSource的ObjectDataSource控件,設置該控件調用ProductsBLL class類的GetProducts()方法。由於該GridView是“只讀”的,在UPDATE, INSERT, 和DELETE標簽裡選“(None)”,點完成。

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401245.gif
圖5:設置ObjectDataSource使用ProductsBLL Class類的GetProducts方法

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401393.gif
圖6:在UPDATE, INSERT, 和DELETE標簽裡選“(None)”

  完成設置後,Visual Studio將自動的添加BoundFields以及一個CheckBoxField,刪除ProductID, ProductName, CategoryID,和CategoryName以外的其它列;並且分別將ProductName 和 CategoryName列的HeaderText屬性重命名為“Product” 和 “Category”.在智能標簽裡啟用“分頁”功能.做完這些修改後,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控件上添加3個Button Web控件,設置第一個按鈕的Text屬性 為“Refresh Grid”;第二個按鈕的Text屬性為“Modify Categories (WITH TRANSACTION)”;第三個按鈕的Text屬性為“Modify Categories (WITHOUT 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的設計模式裡,界面看起來和下面的截屏差不多:

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401394.gif
圖7:頁面包含一個GridView控件和三個Button Web控件

為這3個按鈕的Click events事件創建事件處理器,如下:

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();
}

 

  refresh按鈕的Click事件處理器僅僅調用Products GridView的DataBind方法將數據重新綁定到ridView控件.

  第二個事件處理器對products的CategoryID屬性重新賦值,並調用BLL層裡的新的事務方法來執行數據庫更新.我們注意到將每個產品的ProductID值賦給其CategoryID屬性,對最開頭的幾個產品而言沒有任何問題,但隨著ProductID值越變越大,CategoryID的值也越變越大,而Category表裡定義的種類畢竟有限,於是問題就出來了。

  第三個事件處理器也是將ProductID值賦給CategoryID屬性,只是用ProductsTableAdapter的默認的Update方法來更新數據庫. 該Update方法並沒有使用事務來封裝這些命令,所以只要是沒有違背外鍵約束的更新都會執行成功.

  在浏覽器裡登錄該頁面進行驗證.最開始你將看到如圖8所示的畫面,然後點“Modify Categories (WITH TRANSACTION)”.這將導致頁面回傳並試題更新所有products的CategoryID值,這將導致違背外鍵約束(見圖9).

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401374.gif
圖8:Products將顯示在一個分頁的GridView控件裡

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401307.gif
圖9:導致違背外鍵約束

  現在點擊浏覽器的Back按鈕,再點擊“Refresh Grid”按鈕,此時你看到的界面和圖8的界面一摸一樣。這是因為發生了違背外鍵約束,導致回滾,所有的操作失敗.

  再點“Modify Categories (WITHOUT TRANSACTION)”按鈕,這同樣將違背外鍵約束(見圖9),不過這一次,那些對CategoryID屬性賦以有效值的操作不會回滾.點擊浏覽器的Back按鈕,再點“Refresh Grid”按鈕。就像圖10顯示的那樣,最開始的8個產品的CategoryID值已經發生了更改,比如,在圖8裡Chang的CategoryID值為1,而在圖10裡就變成了2了.

https://www.aspphp.online/bianchen/UploadFiles_4619/201701/2017010916401408.gif
圖10:某些Product的CategoryID值發生了改變,而其它的沒有

結語:

  默認情況下,TableAdapter的方法沒有使用事務來執行數據庫命令,不過只需多做一點工作我們就可以添加一些用於創建、提交、回滾事務的方法.在本教程,我們在ProductsTableAdapter class類裡創建了這3個方法:BeginTransaction, CommitTransaction,和RollbackTransaction.我們考察了如何在try...catch模塊裡使用這些方法來執行一系列的修改命令.具體來說,我們在ProductsTableAdapter裡創建了UpdateWithTransaction方法,該方法運用Batch Update模式對ProductsDataTable裡的每行記錄執行必要的更改操作;我們也對BLL裡的ProductsBLL class類添加了DeleteProductsWithTransaction方法,它將一系列ProductID值作為輸入參數,並使用DB-Direct模式將每個產品刪除.這些方法開始都創建一個事務,再在try...catch模塊裡執行數據更改命令.如果拋出異常,則回滾事務,否則提交事務.

  第五步演示了事務的作用。在接下來的3章我們將以本章為基礎,創建批更新、批刪除、批添加的用戶界面.

  祝編程快樂!

作者簡介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的書,是4GuysFromRolla.com的創始人,自1998年以來一直應用 微軟Web技術。大家可以點擊查看全部教程《[翻譯]Scott Mitchell 的ASP.NET 2.0數據教程》,希望對大家的學習ASP.NET有所幫助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved