共用方式為


建立資料存取層 (VB)

作者 :Scott Mitchell

下載 PDF

在本教學課程中,我們將從頭開始,並使用具類型的 DataSets 建立 DAL (DAL) ,以存取資料庫中的資訊。

簡介

身為 Web 開發人員,我們的生活會圍繞使用數據。 我們會建立資料庫來儲存數據、用來擷取和修改的程式代碼,以及收集並摘要它的網頁。 這是冗長系列中的第一個教學課程,將探索在 ASP.NET 2.0 中實作這些常見模式的技術。 我們將從使用具類型數據集、強制執行自定義商務規則的商務 (規則層) BLL) ,以及由共用通用頁面版面配置 ASP.NET 頁面組成的呈現層,開始建立由 Data Access Layer (DAL) 組成的 軟體架構 。 一旦配置此後端基礎,我們將移至報告,示範如何顯示、摘要、收集和驗證 Web 應用程式的數據。 這些教學課程旨在簡潔,並提供具有許多螢幕快照的逐步指示,以可視化方式引導您完成程式。 每個教學課程都可在 C# 和 Visual Basic 版本中取得,並包含已使用的完整程式代碼下載。 (本第一個教學課程相當冗長,但其餘部分則會以更可摘要的區塊呈現。)

針對這些教學課程,我們將使用位於 App_Data 目錄中的 Microsoft SQL Server 2005 Express Edition 版本的 Northwind 資料庫。 除了資料庫檔案之外, App_Data 資料夾也包含用來建立資料庫的 SQL 腳本,以防您想要使用不同的資料庫版本。 如果您使用不同的 Northwind 資料庫 SQL Server 版本,則必須更新NORTHWNDConnectionString應用程式Web.config檔案中的設定。 Web 應用程式是使用 Visual Studio 2005 Professional Edition 作為文件系統型網站專案所建置。 不過,所有教學課程都同樣適用於 Visual Studio 2005 免費版 Visual Web Developer

在本教學課程中,我們將從頭開始建立數據存取層 (DAL) ,然後在第二個教學課程中建立 商業規則層 (BLL) ,然後在第三個教學課程中處理 頁面配置和流覽 。 第三個之後的教學課程將建立在前三個基礎之上。 我們在此第一個教學課程中有許多要討論的內容,因此請啟動Visual Studio,讓我們開始吧!

步驟 1:建立 Web 專案並連線至資料庫

在我們可以建立數據存取層 (DAL) 之前,我們必須先建立網站並設定資料庫。 從建立以系統為基礎的新文件系統 ASP.NET 網站開始。 若要完成此作業,請移至 [檔案] 功能表,然後選擇 [新增網站],並顯示 [新增網站] 對話框。 選擇 ASP.NET 網站範本、將 [位置] 下拉式清單設定為 [檔案系統]、選擇要放置網站的資料夾,並將語言設定為Visual Basic。

System-Based 網站建立新檔案

圖 1:建立新的檔案 System-Based 網站 (按兩下即可檢視完整大小的影像)

這會建立具有 ASP.NET 頁面、App_Data資料夾和Web.config檔案的新網站Default.aspx

建立網站之後,下一個步驟是在Visual Studio的伺服器總管中新增資料庫的參考。 藉由將資料庫新增至伺服器總管,您可以從 Visual Studio 內新增數據表、預存程式、檢視等等。 您也可以透過查詢產生器,手動或以圖形方式檢視數據表數據或建立自己的查詢。 此外,當我們建置 DAL 的具型別數據集時,我們必須將 Visual Studio 指向應該從中建構具型別數據集的資料庫。 雖然我們可以在該時間點提供此連線資訊,但Visual Studio會自動填入已在 [伺服器總管] 中註冊的資料庫下拉式清單。

將 Northwind 資料庫新增至 [伺服器總管] 的步驟取決於您要在資料夾中使用 SQL Server 2005 Express Edition 資料庫App_Data,或者您是否有想要改用的 Microsoft SQL Server 2000 或 2005 資料庫伺服器設定。

在資料夾中使用資料庫App_Data

如果您沒有 SQL Server 2000 或 2005 資料庫伺服器可連線,或只是想要避免將資料庫新增至資料庫伺服器,則可以使用位於所下載網站App_Data資料夾 () NORTHWND.MDF 的 Northwind 資料庫 SQL Server 2005 Express Edition 版本。

放置於 App_Data 資料夾中的資料庫會自動新增至 [伺服器總管]。 假設您已在計算機上安裝 SQL Server 2005 Express Edition,您應該會看到名為 NORTHWND 的節點。伺服器總管中的 MDF,您可以展開並探索其數據表、檢視、預存程式等 (請參閱圖 2) 。

資料夾App_Data也可以保存 Microsoft Access .mdb 檔案,就像其 SQL Server 對應項目一樣,會自動新增至 [伺服器總管]。 如果您不想使用任何 SQL Server 選項,您一律可以安裝 Northwind Traders 資料庫和應用程式,並放入App_Data目錄。 不過,請記住,Access 資料庫的功能不如 SQL Server 豐富,也不會設計成在網站案例中使用。 此外,有數個以上的 35 個教學課程會利用 Access 不支援的特定資料庫層級功能。

連接到 Microsoft SQL Server 2000 或 2005 資料庫伺服器中的資料庫

或者,您可以連線到安裝在資料庫伺服器上的 Northwind 資料庫。 如果資料庫伺服器尚未安裝 Northwind 資料庫,您必須先執行本教學課程下載中包含的安裝腳本,將其新增至資料庫伺服器。

安裝資料庫之後,請移至 Visual Studio 中的 [伺服器總管],以滑鼠右鍵按兩下 [資料 Connections] 節點,然後選擇 [新增連線]。 如果您沒有看到 [伺服器總管],請移至 [檢視/伺服器總管],或按 Ctrl+Alt+S。 這會顯示 [新增連線] 對話框,您可以在其中指定要連接的伺服器、驗證資訊和資料庫名稱。 成功設定資料庫連接資訊並按兩下 [確定] 按鈕之後,資料庫將會新增為 [資料 Connections] 節點下方的節點。 您可以展開資料庫節點來探索其數據表、檢視、預存程式等等。

將連線新增至資料庫伺服器的 Northwind 資料庫

圖 2:將連線新增至資料庫伺服器的 Northwind 資料庫

步驟 2:建立數據存取層

使用數據時,其中一個選項是直接將數據特定邏輯內嵌至 Web 應用程式中的呈現層 (,ASP.NET 頁面會構成呈現層) 。 這可以是在 ASP.NET 頁面的程式代碼部分或使用標記部分的 SqlDataSource 控制件撰寫 ADO.NET 程式代碼的形式。 不論是哪一種情況,此方法都緊密結合數據存取邏輯與表示層。 不過,建議的方法是分隔數據存取邏輯與呈現層。 此個別層稱為「數據存取層」、「DAL」,簡稱為 DAL,而且通常會實作為個別的「類別庫」專案。 本教學課程結尾的一節中記載此分層架構的優點 (請參閱本教學課程結尾的一節,以了解這些優點) ,也是我們將在此系列中採用的方法。

基礎數據源特有的所有程序代碼,例如建立與資料庫的連線、發出 SELECTINSERTUPDATEDELETE 命令等等,都應該位於 DAL 中。 表示層不應包含這類數據存取程式代碼的任何參考,但應該改為針對任何和所有數據要求呼叫 DAL。 數據存取層通常包含存取基礎資料庫數據的方法。 例如,Northwind 資料庫具有 ProductsCategories 數據表,可記錄要銷售的產品及其所屬的類別。 在我們的 DAL 中,我們將有如下的方法:

  • GetCategories(), 這會傳回所有類別的相關信息
  • GetProducts(),這會傳回所有產品的相關信息
  • GetProductsByCategoryID(categoryID),這會傳回屬於指定類別的所有產品
  • GetProductByProductID(productID),這會傳回特定產品的相關信息

叫用這些方法時,會連線到資料庫、發出適當的查詢,並傳回結果。 我們傳回這些結果的方式很重要。 這些方法可能只會傳回由資料庫查詢填入的 DataSet 或 DataReader,但最好使用 強型別物件傳回這些結果。 強型別對像是架構在編譯時期嚴格定義的物件,相反的是鬆散型別物件,則是在運行時間之前不知道其架構的物件。

例如,根據預設,DataReader 和 DataSet) (是鬆散類型的對象,因為它們的架構是由用來填入這些對象的資料庫查詢所傳回的數據行所定義。 若要從鬆散類型的 DataTable 存取特定數據行,我們需要使用語法,例如: DataTable.Rows(index)("columnName")。 在此範例中,DataTable 的鬆散類型會以我們需要使用字串或序數索引來存取數據行名稱的事實顯示。 另一方面,強型別 DataTable 會將其每個數據行實作為屬性,因而產生類似的程式代碼: DataTable.Rows(index).columnName

若要傳回強型別對象,開發人員可以建立自己的自定義商務物件或使用具類型的數據集。 商務物件是由開發人員實作為類別,其屬性通常會反映商務物件所代表之基礎資料庫數據表的數據行。 具型別的 DataSet 是 Visual Studio 根據資料庫架構為您產生的類別,其成員會根據這個架構進行強型別。 具類型的 DataSet 本身是由擴充 ADO.NET DataSet、DataTable 和 DataRow 類別的類別所組成。 除了強型別的 DataTable 之外,具型別的 DataSet 現在也包含 TableAdapters,這些類別包含用來填入 DataSet 的 DataTable,並將 DataTable 中的修改傳播回資料庫。

注意

如需使用具型別數據集與自定義商務物件之優缺點的詳細資訊,請參閱 設計數據層元件和透過層傳遞數據

我們將針對這些教學課程的架構使用強型別的數據集。 圖 3 說明使用具型別數據集之應用程式不同層級之間的工作流程。

所有數據存取代碼都會重新降級為 DAL

圖 3:所有數據存取碼都會重新分派至 DAL (按兩下即可檢視完整大小的影像)

建立具類型的數據集和數據表配接器

為了開始建立 DAL,首先會將具類型的數據集新增至專案。 若要完成此作業,請以滑鼠右鍵按兩下 方案總管 中的項目節點,然後選擇 [新增專案]。 從樣本清單中選取 [資料集] 選項,並將其命名為 Northwind.xsd

選擇將新的數據集新增至您的專案

圖 4:選擇將新的資料集新增至專案 (按兩下即可檢視完整大小的影像)

按兩下 [新增] 之後,當系統提示您將DataSet新增至 App_Code 資料夾時,請選擇[是]。 然後會顯示具型別數據集的 Designer,而 TableAdapter 組態精靈將會啟動,讓您將第一個 TableAdapter 新增至具類型的數據集。

具型別數據集可作為強型別的數據收集;它是由強型別的DataTable實例所組成,每個實例都會由強型別的DataRow實例組成。 我們將為每個基礎資料庫數據表建立強型別的 DataTable,以供本教學課程系列使用。 讓我們從建立數據表的 Products DataTable 開始。

請記住,強型別的 DataTable 不包含如何從其基礎資料庫數據表存取數據的任何資訊。 為了擷取要填入DataTable的數據,我們會使用TableAdapter類別作為數據存取層。 針對我們的 Products DataTable,TableAdapter 將包含方法 GetProducts()GetProductByCategoryID(categoryID)等等,我們將從表示層叫用。 DataTable 的角色是做為用來在圖層之間傳遞數據的強型別物件。

TableAdapter 組態精靈會提示您選取要使用的資料庫。 下拉式清單會顯示 [伺服器總管] 中的這些資料庫。 如果您未將 Northwind 資料庫新增至 [伺服器總管],您可以此時按兩下 [新增連線] 按鈕來執行此動作。

從 Drop-Down 清單中選擇 Northwind 資料庫

圖 5:從 [Drop-Down 列表] 中選擇 Northwind 資料庫 (按兩下以檢視大小完整的影像)

選取資料庫並按兩下一步, 之後,系統會詢問您是否要將 連接字串 儲存在檔案中Web.config。 藉由儲存 連接字串,您將避免在 TableAdapter 類別中硬式編碼,如此一來,如果未來 連接字串 信息變更,就會簡化事項。 如果您選擇將 連接字串 儲存在組態檔中,該配置檔位於 <connectionStrings> 區段中,您可以選擇性地加密以改善安全性,或稍後透過 IIS GUI 管理員 Tool 中的新 ASP.NET 2.0 屬性頁進行修改,這更適合系統管理員使用。

將連接字串儲存至 Web.config

圖 6:將連接字串儲存 (Web.config按一下以檢視完整大小的映像)

接下來,我們需要定義第一個強型別 DataTable 的架構,併為 TableAdapter 提供填入強型別數據集時要使用的第一個方法。 這兩個步驟可以同時完成,方法是建立查詢,以從我們想要反映在 DataTable 中的數據表傳回數據行。 在精靈結束時,我們會為此查詢提供方法名稱。 完成之後,就可以從我們的呈現層叫用這個方法。 方法會執行定義的查詢,並填入強型別的 DataTable。

若要開始定義 SQL 查詢,我們必須先指出 TableAdapter 發出查詢的方式。 我們可以使用臨機操作 SQL 語句、建立新的預存程式,或使用現有的預存程式。 針對這些教學課程,我們將使用臨機操作 SQL 語句。

使用臨機操作 SQL 語句查詢數據

圖 7:使用臨機操作 SQL 語句查詢數據 (按兩下即可檢視完整大小的影像)

此時,我們可以手動輸入 SQL 查詢。 在 TableAdapter 中建立第一個方法時,您通常想要讓查詢傳回需要以對應 DataTable 表示的數據行。 我們可以藉由建立查詢來傳回數據表中的所有數據列和所有數據列 Products 來完成此作業:

在文字框中輸入 SQL 查詢

圖 8:在文字框中輸入 SQL 查詢, (按兩下即可檢視大小完整的影像)

或者,使用查詢產生器並以圖形方式建構查詢,如圖 9 所示。

透過 查詢編輯器 以圖形方式建立查詢

圖 9:以圖形方式建立查詢,透過 查詢編輯器 (按兩下即可檢視大小完整的影像)

建立查詢之後,但在移至下一個畫面之前,請按兩下 [進階選項] 按鈕。 在網站專案中,[產生插入]、[更新] 和 [刪除語句] 是預設唯一選取的進階選項;如果您從類別庫或 Windows 專案執行此精靈,也會選取 [使用開放式並行存取] 選項。 目前未核取 [使用開放式並行存取] 選項。 我們將在未來的教學課程中檢查開放式並行存取。

僅選取 [產生插入]、[更新] 和 [刪除語句] 選項

圖 10:選取 [只產生插入]、[更新] 和 [刪除語句] 選項 (按兩下即可檢視完整大小的映像)

確認進階選項之後,按 [下一步] 以繼續進行最終畫面。 在這裡,我們會要求您選取要新增至 TableAdapter 的方法。 填入資料有兩種模式:

  • 使用此方法填入 DataTable ,系統會建立方法以取得 DataTable 做為參數,並根據查詢的結果填入它。 例如,ADO.NET DataAdapter 類別會使用其 Fill() 方法實作此模式。
  • 使用此方法傳回 DataTable,此方法會為您建立並填滿 DataTable,並將它當做方法傳回值傳回。

您可以讓 TableAdapter 實作其中一個或兩種模式。 您也可以重新命名這裡提供的方法。 讓我們讓這兩個複選框保持核取狀態,即使我們在整個教學課程中只會使用後者模式。 此外,讓我們將相反的泛型 GetData 方法重新命名為 GetProducts

如果核取,則最後一個複選框 「GenerateDBDirectMethods」會建立 Insert()TableAdapter 的、 Update()Delete() 方法。 如果您取消核取此選項,所有更新都必須透過 TableAdapter 的唯 Update() 一方法來完成,此方法會採用 Typed DataSet、DataTable、單一 DataRow 或 DataRows 陣列。 (如果您已從圖 9 的進階屬性取消核取 [產生插入、更新和刪除語句] 選項,此複選框的設定將不會有任何作用。) 讓我們保留此複選框。

將方法名稱從 GetData 變更為 GetProducts

圖 11:將 [方法名稱] 從 GetData 變更為 GetProducts ([ 按兩下] 以檢視大小完整的影像)

按兩下 [完成] 來完成精靈。 精靈關閉之後,我們會返回 DataSet Designer,其中顯示我們剛才建立的 DataTable。 您可以在 DataTable (、 等) 中看到資料列清單Products,以及 (Fill()GetProducts()) 的方法ProductsTableAdapterProductNameProductID

Products DataTable 和 ProductsTableAdapter 已新增至具類型的數據集

圖 12:D ataTable ProductsProductsTableAdapter 已新增至具類型的數據集 (按兩下即可檢視完整大小的映像)

此時,我們有具有單一 DataTable () Northwind.Products 的具型別數據集,以及具有方法) 強型別 DataAdapter 類別 NorthwindTableAdapters.ProductsTableAdapter (GetProducts() 。 這些物件可用來從程式代碼存取所有產品的清單,例如:

Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim products as Northwind.ProductsDataTable
products = productsAdapter.GetProducts()
For Each productRow As Northwind.ProductsRow In products
    Response.Write("Product: " & productRow.ProductName & "<br />")
Next

此程式代碼不需要我們撰寫一位數據存取特定程序代碼。 我們不需要具現化任何 ADO.NET 類別,就不需要參考任何連接字串、SQL 查詢或預存程式。 相反地,TableAdapter 會為我們提供低階數據存取碼。

此範例中使用的每個物件也是強型別,可讓Visual Studio提供IntelliSense和編譯時間類型檢查。 而且,TableAdapter 傳回的所有 DataTable 都可以系結至 ASP.NET 數據 Web 控件,例如 GridView、DetailsView、DropDownList、CheckBoxList 和其他數個控件。 下列範例說明將 方法傳 GetProducts() 回的 DataTable 系結至 GridView,只包含事件處理程式內 Page_Load 掃描的三行程式代碼。

AllProducts.aspx

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="AllProducts.aspx.vb"
    Inherits="AllProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>View All Products in a GridView</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h1>
            All Products</h1>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

AllProducts.aspx.vb

Imports NorthwindTableAdapters
Partial Class AllProducts
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Load
        Dim productsAdapter As New ProductsTableAdapter
        GridView1.DataSource = productsAdapter.GetProducts()
        GridView1.DataBind()
    End Sub
End Class

產品清單會顯示在 GridView 中

圖 13:產品清單會顯示在 GridView (按兩下以檢視完整大小的影像)

雖然此範例需要在 ASP.NET 頁面的 Page_Load 事件處理程式中撰寫三行程序代碼,但在未來的教學課程中,我們將探討如何使用 ObjectDataSource 以宣告方式從 DAL 擷取數據。 有了 ObjectDataSource,我們不需要撰寫任何程式代碼,也會收到分頁和排序支援!

步驟 3:將參數化方法新增至數據存取層

此時,我們的 ProductsTableAdapter 類別有一個方法, GetProducts()它會傳回資料庫中的所有產品。 雖然能夠處理所有產品絕對有用,但有時候我們想要擷取特定產品的相關信息,或屬於特定類別的所有產品。 若要將這類功能新增至我們的數據存取層,我們可以將參數化方法新增至 TableAdapter。

讓我們新增 GetProductsByCategoryID(categoryID) 方法。 若要將新方法新增至 DAL,請返回 DataSet Designer,在 區段中按兩下滑鼠右鍵ProductsTableAdapter,然後選擇 [新增查詢]。

以滑鼠右鍵按下 TableAdapter 並選擇 [新增查詢]

圖 14:在 TableAdapter 上 Right-Click 並選擇 [新增查詢]

我們會先提示您使用臨機操作 SQL 語句或新的或現有的預存程式來存取資料庫。 讓我們選擇再次使用臨機操作 SQL 語句。 接下來,我們會詢問我們想要使用的 SQL 查詢類型。 由於我們想要傳回屬於指定類別的所有產品,因此我們想要撰寫 SELECT 傳回數據列的語句。

選擇建立會傳回數據列的 SELECT 語句

圖 15:選擇建立 SELECT 會傳回數據列的語句 (按兩下即可檢視大小完整的影像)

下一個步驟是定義用來存取數據的 SQL 查詢。 因為我們只想要傳回屬於特定類別的產品,所以我使用 來自的相同SELECT語句,但新增下列WHERE子句:WHERE CategoryID = @CategoryIDGetProducts() 參數 @CategoryID 會向 TableAdapter 精靈指出,我們要建立的方法需要對應類型的輸入參數 (也就是可為 Null 的整數) 。

輸入查詢以只傳回指定類別中的產品

圖 16:輸入查詢以僅傳回指定類別中的產品, (按兩下即可檢視大小完整的影像)

在最後一個步驟中,我們可以選擇要使用的數據存取模式,以及自定義產生的方法名稱。 針對填滿模式,讓我們將傳回 DataTable 傳回模式的名稱變更為 FillByCategoryID 和 , (GetX 方法) ,讓我們使用 GetProductsByCategoryID

選擇 TableAdapter 方法的名稱

圖 17:選擇 TableAdapter 方法的名稱, (按兩下即可檢視大小完整的影像)

完成精靈之後,DataSet Designer 包含新的 TableAdapter 方法。

產品現在可以依類別查詢

圖 18:產品現在可以依類別查詢

請花點時間使用相同技術新增 GetProductByProductID(productID) 方法。

您可以從 DataSet Designer 直接測試這些參數化查詢。 以滑鼠右鍵按下 TableAdapter 中的 方法,然後選擇 [預覽數據]。 接下來,輸入要用於參數的值,然後按兩下 [預覽]。

這些屬於「供應項目類別」的產品會顯示

圖 19:屬於[訂用帳戶類別] 的產品會顯示 (按兩下即可檢視大小完整的影像)

GetProductsByCategoryID(categoryID)使用 DAL 中的 方法,我們現在可以建立 ASP.NET 頁面,只顯示指定類別中的這些產品。 下列範例顯示所有位於[訂用帳戶] 類別的產品,其值為 CategoryID 1。

Beverages.aspx

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Beverages.aspx.vb"
    Inherits="Beverages" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h1>Beverages</h1>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

Beverages.aspx.vb

Imports NorthwindTableAdapters
Partial Class Beverages
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Load
        Dim productsAdapter As New ProductsTableAdapter
        GridView1.DataSource =
         productsAdapter.GetProductsByCategoryID(1)
        GridView1.DataBind()
    End Sub
End Class

顯示 [飲料類別] 中的這些產品

圖 20:[訂用帳戶類別] 中的這些產品會顯示 (按鍵即可檢視全尺寸影像)

步驟 4:插入、更新和刪除數據

有兩種模式常用於插入、更新和刪除數據。 第一個 INSERT將呼叫資料庫直接模式的模式牽涉到建立方法,在叫用時發出、 UPDATEDELETE 命令給在單一資料庫記錄上運作的資料庫。 這類方法通常會在一系列純量值中傳遞, (整數、字串、布爾值、DateTimes 等) 對應至要插入、更新或刪除的值。 例如,透過數據表的 Products 這個模式,delete 方法會採用整數參數,指出 ProductID 要刪除之記錄的 ,而 insert 方法會採用 的字串 ProductName、的十進位 UnitPrice數、的整數 UnitsOnStock等等。

每個插入、更新和刪除要求都會立即傳送至資料庫

圖 21:每個插入、更新和刪除要求都會立即傳送至資料庫, (按兩下即可檢視大小完整的映射)

另一種將稱為批次更新模式的模式,就是在一個方法呼叫中更新整個 DataSet、DataTable 或 DataRows 集合。 使用此模式時,開發人員會刪除、插入和修改 DataTable 中的 DataRows,然後將那些 DataRows 或 DataTable 傳遞至更新方法。 然後,這個方法會列舉傳入的 DataRow,判斷它們是否已透過 DataRow 的 RowState 屬性值) 修改、新增 (或刪除,併發出每個記錄的適當資料庫要求。

叫用 Update 方法時,所有變更都會與資料庫同步處理

圖 22:當叫用更新方法時,所有變更都會與資料庫同步處理, (按兩下即可檢視大小完整的映像)

TableAdapter 預設會使用批次更新模式,但也支援 DB 直接模式。 因為我們在建立 TableAdapter 時,從 [進階屬性] 中選取 [產生插入]、[更新] 和 [刪除語句] 選項,所以 ProductsTableAdapter 會包含實 Update() 作批次更新模式的方法。 具體而言,TableAdapter 包含 Update() 可以傳遞 Typed DataSet、強型別 DataTable 或一或多個 DataRows 的方法。 如果您在第一次建立 TableAdapter 時,保留 [GenerateDBDirectMethods] 複選框,則也會透過 Insert()Update()Delete() 方法來實作 DB 直接模式。

這兩種數據修改模式都會使用 TableAdapter 的 InsertCommandUpdateCommandDeleteCommand 屬性,向資料庫發出其 INSERTUPDATEDELETE 命令。 您可以按下 DataSet Designer 中的 TableAdapter,然後移至 屬性視窗,以檢查和修改InsertCommandUpdateCommandDeleteCommand 屬性。 (確定您已選取 TableAdapter,而且對像是ProductsTableAdapter 屬性視窗.) 下拉式清單中選取的物件

TableAdapter 具有 InsertCommand、UpdateCommand 和 DeleteCommand 属性

圖 23:TableAdapter 具有 InsertCommandUpdateCommand和 屬性, (DeleteCommand按兩下即可檢視完整大小的影像)

若要檢查或修改上述任一資料庫命令屬性,請按下 CommandText 子屬性,這會啟動查詢產生器。

在查詢產生器中設定 INSERT、UPDATE 和 DELETE 語句

圖 24:在 INSERT查詢產生器中設定 、 UPDATEDELETE 語句, (按兩下即可檢視完整大小的映射)

下列程式代碼範例示範如何使用批次更新模式,將所有產品的價格加倍,且有 25 個單位的庫存或更少:

Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim products As Northwind.ProductsDataTable = productsAdapter.GetProducts()
For Each product As Northwind.ProductsRow In products
   If Not product.Discontinued AndAlso product.UnitsInStock <= 25 Then
      product.UnitPrice *= 2
   End if
Next
productsAdapter.Update(products)

下列程式代碼說明如何使用 DB 直接模式,以程式設計方式刪除特定產品,然後更新一個產品,然後新增一個產品:

Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
productsAdapter.Delete(3)
productsAdapter.Update( _
    "Chai", 1, 1, "10 boxes x 20 bags", 18.0, 39, 15, 10, false, 1)
productsAdapter.Insert( _
    "New Product", 1, 1, "12 tins per carton", 14.95, 15, 0, 10, false)

建立自定義插入、更新和刪除方法

Insert()DB 直接方法所建立的 、 Update()Delete() 方法可能有點麻煩,特別是對於具有許多數據行的數據表。 查看先前的程式代碼範例,如果沒有 IntelliSense 的協助,它並不特別清楚哪些 Products 數據表數據行對應至 Update()Insert() 方法的每個輸入參數。 有時候,當我們只想要更新單一數據行或兩個數據行,或想要自定義 Insert() 的方法,或許會傳回新插入記錄 IDENTITY (自動遞增) 字段的值。

若要建立這類自定義方法,請返回DataSet Designer。 以滑鼠右鍵按下 TableAdapter,然後選擇 [新增查詢],返回 TableAdapter 精靈。 在第二個畫面上,我們可以指出要建立的查詢類型。 讓我們建立一個方法,以新增新產品,然後傳回新加入記錄的值 ProductID。 因此,選擇建立 INSERT 查詢。

建立方法以將新數據列新增至 Products 數據表

圖 25:建立方法,將新數據列新增至 Products 數據表 (按兩下即可檢視完整大小的影像)

在下一個畫面上,InsertCommand會出現 。CommandText 藉由在查詢結尾新增來增強 SELECT SCOPE_IDENTITY() 此查詢,這會傳回插入相同範圍中數據行的最後一個 IDENTITY 識別值。 (如需詳細資訊,請參閱 技術檔SCOPE_IDENTITY() 以取得您可能想要 使用SCOPE_IDENTITY () 取代 @@IDENTITY.) 在新增 SELECT 語句之前,請務必使用分號結束INSERT語句。

增強查詢以傳回SCOPE_IDENTITY () 值

圖 26:增強查詢以傳回 SCOPE_IDENTITY() 值 (按兩下以檢視大小完整的影像)

最後,將新方法 InsertProduct命名為 。

將 [新增方法名稱] 設定為 InsertProduct

圖 27:將 [新增方法名稱] 設定為 InsertProduct [ (按兩下以檢視大小完整的影像)

當您傳回 DataSet Designer 您會看到 包含ProductsTableAdapter新方法的 InsertProduct。 如果這個新方法沒有數據表中每個數據行的參數 Products ,您可能會忘記以分號終止 INSERT 語句。 InsertProduct設定 方法,並確定您有分隔 和 SELECT 語句的INSERT分號。

根據預設,插入方法會發出非查詢方法,這表示它們會傳回受影響的數據列數目。 不過,我們想要 InsertProduct 方法傳回查詢所傳回的值,而不是受影響的數據列數目。 若要完成這項作業,請將 InsertProduct 方法的 ExecuteMode 屬性調整為 Scalar

將 ExecuteMode 屬性變更為 Scalar

圖 28:將 [屬性] 變更 ExecuteModeScalar [ (按兩下以檢視完整大小的影像)

下列程式代碼顯示這個新的 InsertProduct 方法運作方式:

Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim new_productID As Integer = Convert.ToInt32(productsAdapter.InsertProduct( _
    "New Product", 1, 1, "12 tins per carton", 14.95, 10, 0, 10, false))
productsAdapter.Delete(new_productID)

步驟 5:完成數據存取層

請注意,類別ProductsTableAdaptersCategoryIDProducts數據表傳回 和 SupplierID 值,但不包含CategoryNameCategories數據表中的數據行或數據表中的數據CompanyNameSuppliers行,雖然這些可能是我們在顯示產品資訊時想要顯示的數據行。 我們可以增強 TableAdapter 的初始方法 GetProducts(),以同時包含 CategoryNameCompanyName 數據行值,這會更新強型別的 DataTable 以包含這些新數據行。

不過,這可能會產生問題,因為 TableAdapter 的插入、更新和刪除數據的方法是以這個初始方法為基礎。 幸運的是,自動產生的插入、更新和刪除方法不會受到 子句中的 SELECT 子查詢影響。 藉由小心將查詢新增至 CategoriesSuppliers 作為子查詢, JOIN 而不是 ,我們將避免修改這些方法以修改數據。 以滑鼠右鍵按兩下 中的 ProductsTableAdapter 方法,GetProducts()然後選擇 [設定]。 然後,調整 SELECT 子句,使其看起來像這樣:

SELECT     ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories
WHERE Categories.CategoryID = Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers
WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName
FROM         Products

更新 GetProducts () 方法的 SELECT 語句

圖 29:更新 SELECT 方法的 GetProducts() 語句 (按兩下即可檢視大小完整的映像)

更新 GetProducts() 方法以使用此新查詢之後,DataTable 將包含兩個新的資料行: CategoryNameSupplierName

Products DataTable 有兩個新的數據行

圖 30Products DataTable 有兩個新的數據行

請花點時間更新 SELECT 方法中的 GetProductsByCategoryID(categoryID) 子句。

如果您使用語法更新 GetProducts()SELECTJOIN DataSet Designer 將無法使用 DB 直接模式自動產生插入、更新和刪除資料庫數據的方法。 相反地,您必須手動建立它們,就像本 InsertProduct 教學課程稍早使用方法一樣。 此外,如果您想要使用批次更新模式,您必須手動提供 InsertCommandUpdateCommandDeleteCommand 屬性值。

新增剩餘的 TableAdapters

到目前為止,我們只會查看使用單一資料庫數據表的單一 TableAdapter。 不過,Northwind 資料庫包含數個相關數據表,我們需要在 Web 應用程式中使用。 具類型的數據集可以包含多個相關的 DataTable。 因此,若要完成 DAL,我們需要為我們將在這些教學課程中使用的其他數據表新增 DataTable。 若要將新的 TableAdapter 新增至 Typed DataSet,請開啟 DataSet Designer,在 Designer 中按兩下滑鼠右鍵,然後選擇 [新增/ TableAdapter]。 這會建立新的 DataTable 和 TableAdapter,並逐步引導您完成本教學課程稍早所檢查的精靈。

使用下列查詢建立下列 TableAdapters 和方法需要幾分鐘的時間。 請注意,中的 ProductsTableAdapter 查詢包含子查詢,以擷取每個產品的類別和供應商名稱。 此外,如果您已遵循,則已經新增 類別ProductsTableAdapterGetProducts()的 和 GetProductsByCategoryID(categoryID) 方法。

  • ProductsTableAdapter

    • GetProducts

      SELECT     ProductID, ProductName, SupplierID, 
      CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, 
      UnitsOnOrder, ReorderLevel, Discontinued, 
      (SELECT CategoryName FROM Categories WHERE
      Categories.CategoryID = Products.CategoryID) as 
      CategoryName, (SELECT CompanyName FROM Suppliers
      WHERE Suppliers.SupplierID = Products.SupplierID) 
      as SupplierName
      FROM         Products
      
    • GetProductsByCategoryID

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName,
      (SELECT CompanyName FROM Suppliers WHERE
      Suppliers.SupplierID = Products.SupplierID)
      as SupplierName
      FROM         Products
      WHERE      CategoryID = @CategoryID
      
    • GetProductsBySupplierID

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName, 
      (SELECT CompanyName FROM Suppliers WHERE 
      Suppliers.SupplierID = Products.SupplierID) as SupplierName
      FROM         Products
      WHERE SupplierID = @SupplierID
      
    • GetProductByProductID

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName 
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName, 
      (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) 
      as SupplierName
      FROM         Products
      WHERE ProductID = @ProductID
      
  • CategoriesTableAdapter

    • GetCategories

      SELECT     CategoryID, CategoryName, Description
      FROM         Categories
      
    • GetCategoryByCategoryID

      SELECT     CategoryID, CategoryName, Description
      FROM         Categories
      WHERE CategoryID = @CategoryID
      
  • SuppliersTableAdapter

    • GetSuppliers

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      
    • GetSuppliersByCountry

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      WHERE Country = @Country
      
    • GetSupplierBySupplierID

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      WHERE SupplierID = @SupplierID
      
  • EmployeesTableAdapter

    • GetEmployees

      SELECT     EmployeeID, LastName, FirstName, Title,
      HireDate, ReportsTo, Country
      FROM         Employees
      
    • GetEmployeesByManager

      SELECT     EmployeeID, LastName, FirstName, Title, 
      HireDate, ReportsTo, Country
      FROM         Employees
      WHERE ReportsTo = @ManagerID
      
    • GetEmployeeByEmployeeID

      SELECT     EmployeeID, LastName, FirstName, Title,
      HireDate, ReportsTo, Country
      FROM         Employees
      WHERE EmployeeID = @EmployeeID
      

新增四個 TableAdapters 之後的數據集 Designer

圖 31:新增四個 TableAdapters 之後的 DataSet Designer (按兩下即可檢視大小完整的影像)

將自定義程式代碼新增至 DAL

新增至具型別數據集的 TableAdapters 和 DataTable 會以 XML 架構定義檔案表示, (Northwind.xsd) 。 您可以用滑鼠右鍵按兩下 Northwind.xsd 方案總管 中的檔案,然後選擇 [檢視程式代碼] 來檢視此架構資訊。

Northwinds 具型別數據集的 XML 架構定義 (XSD) 檔案

圖 32:Northwinds 具型別數據集的 XML 架構定義 (XSD) 檔案 (按兩下即可檢視大小完整的映像)

在編譯時或在運行時間 (時,此架構資訊會在設計時間轉譯成 C# 或 Visual Basic 程式代碼,) ,此時您可以使用調試程式逐步執行。 若要檢視此自動產生的程式代碼,請移至 [類別檢視],然後向下切入至 TableAdapter 或 Typed DataSet 類別。 如果您沒有在畫面上看到 [類別檢視],請移至 [檢視] 功能表,然後從該處選取它,或按 Ctrl+Shift+C。 從 [類別檢視] 中,您可以看到 Typed DataSet 和 TableAdapter 類別的屬性、方法和事件。 若要檢視特定方法的程式代碼,請按兩下 [類別檢視] 中的方法名稱,或以滑鼠右鍵按兩下它,然後選擇 [移至定義]。

從類別檢視選取 [移至定義] 來檢查自動產生的程序代碼

圖 33:從類別檢視選取 [移至定義] 來檢查自動產生的程式代碼

雖然自動產生的程式代碼是絕佳的節省時間,但程式代碼通常非常泛型,而且必須自定義以符合應用程式的獨特需求。 不過,擴充自動產生的程式代碼的風險是產生程式碼的工具可能會決定「重新產生」並覆寫您的自定義。 使用 .NET 2.0 的新部分類別概念,可以輕鬆地跨多個檔案分割類別。 這可讓我們將自己的方法、屬性和事件新增至自動產生的類別,而不必擔心Visual Studio覆寫我們的自定義專案。

為了示範如何自定義 DAL,讓我們將方法新增 GetProducts()SuppliersRow 類別。 類別 SuppliersRow 代表數據表中的 Suppliers 單一記錄;每個供應商都可以提供零到許多產品,因此 GetProducts() 會傳回指定供應商的這些產品。 若要完成此作業, App_Code 請在名為的資料夾 SuppliersRow.vb 中建立新的類別檔案,並新增下列程式代碼:

Imports NorthwindTableAdapters
Partial Public Class Northwind
    Partial Public Class SuppliersRow
        Public Function GetProducts() As Northwind.ProductsDataTable
            Dim productsAdapter As New ProductsTableAdapter
            Return productsAdapter.GetProductsBySupplierID(Me.SupplierID)
        End Function
    End Class
End Class

這個部分類別會指示編譯程式在建置 Northwind.SuppliersRow 類別時包含 GetProducts() 我們剛才定義的 方法。 如果您建置專案,然後返回類別檢視,您現在會看到 GetProducts() 列為 的方法 Northwind.SuppliersRow

GetProducts () 方法現在是 Northwind.SuppliersRow 類別的一部分

圖 34:方法 GetProducts() 現在是 類別的 Northwind.SuppliersRow 一部分

方法 GetProducts() 現在可用來列舉特定供應商的產品集,如下列程式代碼所示:

Dim suppliersAdapter As New NorthwindTableAdapters.SuppliersTableAdapter()
Dim suppliers As Northwind.SuppliersDataTable = suppliersAdapter.GetSuppliers()
For Each supplier As Northwind.SuppliersRow In suppliers
    Response.Write("Supplier: " & supplier.CompanyName)
    Response.Write("<ul>")
    Dim products As Northwind.ProductsDataTable = supplier.GetProducts()
    For Each product As Northwind.ProductsRow In products
        Response.Write("<li>" & product.ProductName & "</li>")
    Next
    Response.Write("</ul><p> </p>")
Next

此數據也可以顯示在任何 ASP 中。NET 的數據 Web 控制件。 下列頁面使用具有兩個字段的 GridView 控件:

  • 顯示每個供應商名稱的 BoundField,以及
  • TemplateField,其中包含 BulletedList 控件,該控件系結至每個供應商的方法所 GetProducts() 傳回的結果。

我們將檢查如何在未來的教學課程中顯示這類主要詳細數據報表。 現在,此範例的設計目的是要說明如何使用新增至 類別的 Northwind.SuppliersRow 自定義方法。

SuppliersAndProducts.aspx

<%@ Page Language="VB" CodeFile="SuppliersAndProducts.aspx.vb"
    AutoEventWireup="true" Inherits="SuppliersAndProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h1>
            Suppliers and Their Products</h1>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             AutoGenerateColumns="False"
             CssClass="DataWebControlStyle">
                <HeaderStyle CssClass="HeaderStyle" />
                <AlternatingRowStyle CssClass="AlternatingRowStyle" />
                <Columns>
                    <asp:BoundField DataField="CompanyName"
                      HeaderText="Supplier" />
                    <asp:TemplateField HeaderText="Products">
                        <ItemTemplate>
                            <asp:BulletedList ID="BulletedList1"
                             runat="server" DataSource="<%# CType(CType(Container.DataItem, System.Data.DataRowView).Row, Northwind.SuppliersRow).GetProducts() %>"
                                 DataTextField="ProductName">
                            </asp:BulletedList>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

SuppliersAndProducts.aspx.vb

Imports NorthwindTableAdapters
Partial Class SuppliersAndProducts
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles Me.Load
        Dim suppliersAdapter As New SuppliersTableAdapter
        GridView1.DataSource = suppliersAdapter.GetSuppliers()
        GridView1.DataBind()
    End Sub
End Class

供應商的公司名稱列在左側數據行中,其產品位於右側

圖 35:供應商的公司名稱列在左側數據行中,其產品在右側 (按兩下即可檢視大小完整的影像)

摘要

建立建立 DAL 的 Web 應用程式應該是第一個步驟之一,在您開始建立呈現層之前發生。 使用 Visual Studio,根據具型別數據集建立 DAL 是一項工作,可在 10-15 分鐘內完成,而不需要撰寫程式代碼行。 接下來的教學課程將會以這個 DAL 為基礎。 在下一個教學課程中,我們將定義一些商務規則,並瞭解如何在不同的商業規則層中實作規則。

快樂的程序設計!

深入閱讀

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

本教學課程中包含的主題影片訓練

關於作者

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

特別感謝

本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的首席檢閱者是 Ron Green、Azure Giesenow、Dennis Patterson、Liz Shulok、Abel Gomez 和 Carlos Reviewers。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。