posts - 5,  comments - 5,  trackbacks - 0
          簡介

          在本次學習中,我們將會從最基本的開始,建立一個數據訪問層,DAL(Data Access Layer),使用DataSets類,來訪問數據庫中的信息。

          隨著互聯網的發展,我們的生活總是在帶有數據的工作中進行的。我們建立數據庫來存儲數據,編寫代碼來取回或者更改它們,并且我們制作頁面來收集匯總這些數據。這是我們的漫長學習中的第一課,我們將會來探究在asp2.0中實現這些常用的模式的技術。我們將會從建立一個軟件架構開始,該架構包括使用DataSets類的數據訪問層(DAL)、執行客戶業務規則的業務邏輯層(BLL)和一個由asp.net頁構成的表示層,這些表示頁將會共享一些常用的界面風格。當這些后臺的基礎被搭建好后,我們將會來展示如何從一個web應用來顯示、匯總、搜集最終使其變為可用的數據。(第一個教程將會很長,但是剩下的將會被分解為更多的容易理解的小模塊)

          在本次學習中,我們將會使用到MS SQL Server 2005 Express Edition中的Northwind數據庫,它存在于App_Data目錄中。除了數據庫文件,在App_Data文件夾中也包含了建立這個數據庫的SQL腳本,該腳本在你想使用不同版本的數據庫時或許會有用處。如果你想要的話,你也可以從微軟的站點中直接下載。如果你使用的是不同版本的SQL Server中的Northwind數據庫的話,你需要更新NORTHWINDConnectionString設置,該設置在web應用中的Web.config文件里。這個web應用是使用Visual Studio 2005 Professional Edition建立的一個system-baseed網站項目文件。盡管如此,所有的教程都將會很好的運行在免費版本的Visual Studio 2005中,那就是Visual Web Developer.

          在教程(1)中,我們將會從最基本的開始,建立一個數據訪問層(DAL),接下來將會在教程(2)中建立一個業務邏輯層(BLL),在教程(3)中我們將會在頁面和導航中讓它運行起來。在三次的學習之后,我們將會在之前建立的基礎上進行編譯。作為探究的開端,我們將會學習到很多,所以趕快打開你的Visual Studio,開始我們的學習吧!

          第一步:建立一個web項目并且與數據庫相連

          在我們建立數據訪問層(DAL)之前,我們首先需要創建一個站點并且建立數據庫。開始建立一個新的system-based的asp.net站點文件。File→New Web Site,在對話框中選擇ASP.NET Web Site,將Location設置為“File System”,選擇一個文件夾來存放這個站點,并且語言選擇“C#”。

          圖片1:建立新的System-Based站點

          圖片1:建立新的System-Based站點

          這將建立一個新的帶有名為“Default.aspx”的asp.net頁和一個App_Data文件夾的站點。

          建立站點后,下一步是添加在Visual Studio的Server Explorer中對于數據庫的引用。添加數據庫后,你便可以添加表,存儲過程,視圖,和從Visual Studio中取得的任何內容。你同樣可以瀏覽表中的數據,手寫或者利用試圖表示來建立自己的查詢語句。那么在今后當我們為了建立數據訪問層而建立DataSets類的時候,我們就需要將Visual Studio指向這個數據庫,通過這種方式我們才能建立DataSets類。當我們能夠及時地提供這些連接信息的時候,Visual Studio便會在Server Explorer中自動地創建一個由已注冊的數據庫組成的下拉列表。

          如何向Server Explorer中添加Northwind數據庫的步驟取決于你想使用的是App_Data文件夾中的SQL Server 2005 Express Edition版本的數據庫還是SQL Server 2000 或 2005版本的數據庫服務器。

          使用在App_Data文件夾中的數據庫

          如果你沒有一個SQL Server 2000/2005的數據庫服務器用來連接,或者你只是想避免向數據庫服務器中添加數據庫的話,那么你就可以使用Northwind數據庫的SQL Server 2005 Express Edition版本,它存在于被下載的站點的App_Data文件夾中(NORTHWND.MDF)。

          存在于App_Data中的數據庫都會自動地被添加到Server Explorer中。假設在你的機器中安裝了SQL Server 2005 Express Edition,那么在Server Explorer中你應該能看到一個叫做“NORTHWND.MDF”的節點,你可以展開這個節點,查看他包含的表、視圖、存儲過程等等(見圖片2)。

          App_Data文件夾同樣可以保存Access數據庫的.mdb文件,同樣會像SQL Server那樣自動地添加到Server Explore中。如果你不想使用任何的SQL Server的選項的話,你也可以下載一個Access版的Northwind數據庫文件并且把它放在App_Data目錄中。注意,盡管如此,Access數據庫沒有像SQL Server那樣的優勢,并且它也不是被設計用來做web站點的。并且,在35+的兩個教程中將會利用Access不支持的數據庫級別的特點。

          連接MS SQL Server 2000/2005數據庫服務器中的數據庫

          作為選擇,你可能會連接一個建立在數據庫服務器中的Northwind數據庫。如果這個數據庫服務器中還沒有建立Northwind數據庫的話,你首先必須得執行本教程中的建立語句或者直接從微軟的站點下載SQL Server 2000版本的Northwind數據庫和執行語句,然后把它加入到你的數據庫服務器中。

          當你建立了這個數據庫后,在Visual Studio的Server Explorer中,右擊Data Connections節點,點擊"Add Connection"。如果你沒有看到Server Explorer的話,那么你可以點擊View→Server Explorer,或者點擊Ctrl+Alt+S(直到現在我才知道,這里的Server Explorer對應到我所使用的這個Visual Studio中的是Database Explorer)。這將會彈出對話框,通過它你可以制定想要連接的數據庫,一些鑒定的信息和數據庫的名字。一旦你成功地確認了數據庫連接的相關信息并且點擊了[OK]按鈕后,那么這個數據庫將會以節點的形式被添加在Data Connections節點的下邊。你可以展開這些節點,瀏覽它所包含的表、視圖、存儲過程等等。(此處他講的不是很詳細,我把我的操作過程描述一下。在彈出的對話框中選擇“Microsoft SQL Server”,然后點擊[Continue],會出現設置數據庫連接的東東,按照以前的經驗,在Server name中填寫“(local)”,在Log on to the server中選擇Use SQL Server Authentication選項,然后填寫登陸的id和密碼,這時在Connect to a database中就會出現讓你選擇數據庫的下拉列表了,很順利地,我找到了Northwind數據庫。如果你想看看是不是連接成功了,你可以點擊左下角的[Test Connection]按鈕,連接成功后會彈出“Test connection succeeded”的消息框。最后你點擊[OK]按鈕就OK了)

          圖片2:建立同Northwind數據庫的連接

          圖片2:建立同Northwind數據庫的連接

          第二步:建立數據訪問層

          進行數據庫編程的一個方法是直接在表示層中插入代碼(在一個web應用中,表示層是由asp.net頁構成的)。這個或許會采用在asp.net頁中編寫ADO.NET代碼的形勢,或者是使用SqlDataSoruce控件來實現。無論使用哪種方法,都能夠將數據訪問邏輯同表示層緊密地聯系到一起。盡管如此,推薦的方法還是將數據訪問邏輯同表示層分離開來。那么這個被分離出來的數據訪問邏輯就被稱為數據訪問層(Data Access Layer),簡稱DAL,并且被建成了一個特別的分離的類庫工程。這種分層架構的優點是具有很好的文檔性(關于此優點請察看笨教成最后的“更深一層的閱讀”部分)并且是我們將在本教程中使用的方法。

          所有被使用下劃線標記出來的代碼都應該被放在數據訪問層中,比如建立同數據庫的連接,使用SELECT\INSERT\UPDATE\DELETE等命令。表示層中不應該存在任何通數據訪問相關的代碼,但是應該包含對數據訪問層的數據請求的調用。例如,在Northwind數據庫中,它包含了Products和Categories表,表中記錄了出售商品及其所屬的類別。在我們要建立的數據訪問層中,我們將會用到以下的方法:
          GetCategories(),將會返回關于所有類別的信息
          GetProducts(),將會返回所有商品的信息
          GetProductsByCategoryID(categoryID),將會返回所有屬于某一類別的商品信息
          GetProductByProductID(productID),將會返回某一商品的信息

          調用這些方法,將會連接數據庫,調用適當的方法,并且返回結果。重要的是我們如何返回這些結果。這些方法能夠通過執行一些查詢簡單地返回一個DataSet或者DataReader,但是理想的返回結果應該是以強類別對象(strongly-typed objects)的形式返回。強類別對象是指它的圖表在編譯的時候就被嚴格的定義好了,相反的,弱類別對象是指程序在真正地運行之前圖表內容還不知道呢。(這句翻譯得不好,原文是A strongly-typed object is one whose schema is rigidly defined at compile time, whereas the opposite, a loosely-typed object, is one whose schema is not known until runtime.)

          例如,一個弱類別對象的DataReader和DataSet(默認的),他們的圖表是對(通過執行查詢后返回的)列定義,那么如果我們想要訪問一個弱類別定義的DataTable的話,我們需要使用像這樣的語句:DataTable.Rows[index]["columnName"]。那么本例中的DataTable的弱定義就顯現出來了,事實就是,我們不得不通過使用一個字符串或者序號來訪問列的名稱。如果是一個強類別的DataTable的話,將會使它的列作為屬性被實現,我們就可以使用類似下邊這樣的代碼:DataTable.Rows[index].columnName。

          為了返回一個強類別的對象,開發者或者可以創建自己的業務對象,或者可以使用DataSets類。一個業務對象被開發者定義成為一個類,這個類(此處不好翻譯,原文:A business object is implemented by the developer as a class whose properties typically reflect the columns of the underlying database table the business object represents)。一個DataSet類是在一個數據庫圖表的基礎上由Visual Studio為你建立的一個類,這個類的成員根據圖表被強類別(很好的定義)了。DataSet類本身包括由ADO.NET的DataSet,DataTable和DataRow類擴展出來的類。DataSet類同樣包含TableAdapters,它是一個包含能夠實現DataSet的DataTables并且能夠將對DataTables中的修改傳回給數據庫的類。

          我們將會使用強類別的DataSets來構建本教程的架構。圖片3顯示了在一個使用DataSets類的應用中的不同層之間的工作流程。

          圖片3:所有的數據訪問代碼被傳遞給DAL

          圖片3:所有的數據訪問代碼被傳遞給DAL

          建立一個Dataset和table adapter類

          為了開始建立我們的DAL,我們首先向我們的項目中添加一個DataSet類。右擊在Solution Explorer中的Project節點,選擇“Add New Item”。選擇Templates列表中的DataSet選項,并且將其命名為Northwind.xsd。

          圖片4:為你的項目添加一個新的DataSet

          圖片4:為你的項目添加一個新的DataSet

          當點擊[Add]按鈕后,將會詢問你是否將DataSet添加到App_Code文件夾中,選擇是。這個DataSet類的設計視圖就會顯示出來了,并且TableAdapter的配置向導也會開始,它允許你可以向你的DataSet類中添加第一個TableAdapter類。

          DataSet類是作為強類別的數據集和被提供的;它是由強類別的DataTable實例構成的,每個DataTable實例又是由強類別的DataRow實例構成的。我們將會為下邊的數據庫的每個表建立一個強類別的DataTable。讓我們開始為Products表建立一個DataTable吧。

          你要記住的是強類別的DataTables不包含任何的關于如何從他們的基本的數據表中訪問數據的信息。為了找到能夠填入DataTable中的數據,我們用到了TableAdapter類,這個類將起到數據訪問層的作用。為了建立Products的DataTable,TableAdapter將包含GetProducts(),GetProductByCategoryID(categoryID)等方法,這些方法都是我們將要從表示層調用的。這個DataTable的角色就是作為在兩層之間傳遞數據的一個強類別的對象。

          TableAdapter配置向導首先會讓你選擇你將在那個數據庫中工作。這個下拉列表中顯示了在Server Explor中顯示的數據庫。如果你沒有將Northwind數據庫添加到Server Explorer的話,你可以點擊[New connection]按鈕來添加它。

          圖片5:從下拉列表中選擇Northwind數據庫

          圖片5:從下拉列表中選擇Northwind數據庫

          選擇完數據庫并點擊[Next]按鈕后,你將會被詢問是否將連接的字符串保存在Web.config文件中。在保存的時候,你應該避免將代碼硬性地寫入TableAdapter類中,其實在將來連接字符串變動的時候將會簡化一些操作。如果你選擇了將連接字符串保存在配置文件的<connectionStrings>部分里的話,那么你就可以隨意的進行加密,以此來提高安全性,并且在今后你可以通過新的在IIS GUI Admin工具中的asp.net 2.0屬性頁來進行修改,這種方法對于管理者來說是更加理想的。

          圖片6:將連接字符串存入Web.config中

          圖片6:將連接字符串存入Web.config中

          接下來,需要為我們的第一個強類別的DataTable定義一個圖表,并且為TableAdapter提供第一個可使用的方法來填充這個強類別的DataSet。這兩個步驟可以通過建立返回列的查詢來同時完成。查詢是對對我們要映射到DataTable中的表的查詢。在向導的最后我們將會給這次查詢建立一個方法名。當完成上述操作后,這個方法就可以在表示層中被調用了。這個方法將會執行定義的查詢并且返回一個強類別的DataTable。

          在開始定義SQL查詢的時候,我們必須首先指出我們想要如何讓TableAdapter來發出這個查詢。我們可以使用一個ad-hoc SQL 聲明,建立一個新的存儲過程,或者使用一個已經存在的存儲過程。在教程當中,我們將會使用ad-hoc SQL 聲明。

          圖片7:使用Ad-Hoc SQL 聲明來查詢數據

          圖片7:使用Ad-Hoc SQL 聲明來查詢數據

          這個時候我們便可以輸入查詢的語句了。當在TableAdapter中建立第一個方法的時候,你想要返回在相應的DataTable中想要表達的列。我們可以通過返回Products表中所有列的方式來完成以上的工作。

          圖片8:在文本框中輸入SQL查詢語句

          圖片8:在文本框中輸入SQL查詢語句

          另外一種方法可以使用Query Builder和視圖來構建一個查詢,像圖片9所示的那樣。

          圖片9:在查詢編輯器中建立查詢視圖

          圖片9:在查詢編輯器中建立查詢視圖

          建立完查詢后,在到下一個界面之前,點擊[Advance Options]按鈕。在Web Site Projects中,“Generate Insert, Update, and Delete statements”是唯一一個默認選中的選項;如果你是從一個類庫或者一個Windows項目中運行的該向導的話,“Use optimistic concurrency”同樣也會被選中。保持“Use optimistic concurrency”不被選中。

          圖片10:只選中“Generate Insert, Update, and Delete statements”選項

          圖片10:只選中“Generate Insert, Update, and Delete statements”選項

          在確定了這些高級選項后,點擊[Next]按鈕進入最后一部分。這里我們將會被要求選擇向TableAdapter中添加哪種方法。這里有兩種模式來填充數據:

          Fill a DataTable - 這將會建立一種方法,這種方法會把DataTable作為一個參數來接收,并且以查詢的結果作為基礎來填充這個DataTable。例如,ADO.NET的DataAdapter類,就是通過使用它的Fill()方法來實現這種模式的。

          Return a DataTable - 這種方式會將建立并填充一個DataTable并且將這個DataTable作為返回的值。

          你可以讓TableAdapter使用上述的一種或兩種模式來實現。你也可以重新給這些方法命名。讓我們保持兩個選項都被選中,盡管我們在這里只是用后邊的這種模式。并且讓我們重新命名一下這個名字不太特殊的GetData方法,重命名為“GetProducts”。

          如果選中了最后那個選項,將會為這個TableAdapter建立Insert()、Update()和Delete()方法。如果你沒有選中它的話,所有的更新將需要通過TableAdapter的Update()方法來執行,這種方法接受DataSet類,一個DataTable,一個單獨的DataRow或者一組DataRow。(如果你在圖片9中就沒有選擇“Generate Insert, Update, and Delete statements”選項的話,那么這個選項也將不起作用)。保持這個選項被選中。

          圖片11:將方法重新命名為“GetProducts”

          圖片11:將方法重新命名為“GetProducts”

          點擊[Finish]按鈕完成安裝向導。當向導關閉后我們將會返回到DataSet的設計視圖,在這個視圖中顯示了我們剛剛建立的那個DataTable。你可以看到在Products的DataTable的列的列表(ProductID,ProductName,等等),還有ProductsTableAdapter的方法(Fill()和GetProducts())。

          圖片12:Products DataTable和ProductsTableAdapter也被添加到了DataSet類中

          圖片12:Products DataTable和ProductsTableAdapter也被添加到了DataSet類中

          現在我們有了一個包含一個單獨的DataTable(Northwind.Products)的DataSet類和一個強類別的DataAdapter類(NorthwindTableAdapters.ProductsTableAdapter),這個類還包含一個GetProducts()的方法。通過以下的代碼這些對象可以用來訪問所有的商品(Product)。

          C#

          ?

          NorthwindTableAdapters.ProductsTableAdapter?productsAdapter?=?new?NorthwindTableAdapters.ProductsTableAdapter();
          Northwind.ProductsDataTable?products;

          products?
          =?productsAdapter.GetProducts();

          foreach?(Northwind.ProductsRow?productRow?in?products)
          ????Response.Write(
          "Product:?"?+?productRow.ProductName?+?"<br?/>");

          這并不需要我們自己手寫這些代碼。我們不需要實例化任何ADO.NET類,我們不需要關聯任何連接字符串、SQL查詢或者存儲過程。取而代之的是TableAdapter已經為我們生成了這些簡易的數據訪問代碼。

          這個例子中使用的每個對象都是強類別的,允許Visual Studio提供智能和實時編譯的類別檢查。并且這些TableAdapter返回的DataTables可以被綁定到asp.net的web控件上,就像GridView,DateilsView,DropDownList,CheckBoxList等等。接下來的例子將展示如何把通過GetProducts()方法返回的DataTable綁定到GridView控件上,這只需要在Page_Load事件中編寫不到3行的代碼。

          AllProducts.aspx

          <%@?Page?Language="C#"?AutoEventWireup="true"?CodeFile="AllProducts.aspx.cs"?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>
          ????????????
          &nbsp;</p>

          ????
          </div>
          ????
          </form>
          </body>
          </html>

          AllProducts.aspx.cs

          using?System;
          using?System.Data;
          using?System.Configuration;
          using?System.Collections;
          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;
          using?NorthwindTableAdapters;

          public?partial?class?AllProducts?:?System.Web.UI.Page
          {
          ????
          protected?void?Page_Load(object?sender,?EventArgs?e)
          ????
          {
          ????????ProductsTableAdapter?productsAdapter?
          =?new
          ?????????ProductsTableAdapter();
          ????????GridView1.DataSource?
          =?productsAdapter.GetProducts();
          ????????GridView1.DataBind();
          ????}

          }

          圖片13:在GridView中顯示的Products列表

          圖片13:在GridView中顯示的Products列表

          這個例子需要我們在asp.net頁的Page_Load事件中寫3行代碼,在將來的學習中,我們將了解如何使用ObjectDataSource從數據訪問層顯式地取回數據。通過ObjectDataSource我們不再需要寫任何代碼并且還能很好地獲得分頁的功能!

          第三步:向數據訪問層添加帶參數的方法

          現在我們的ProductsTableAdapter類只包含一個方法,GetProducts(),這個方法返回數據庫中的所有商品。但是有時我們只想返回某一特殊商品的信息,或者只屬于某種類別的商品信息。我們可以向TableAdapter中添加帶參數的方法來實現向數據訪問層添加這種功能。

          首先我們添加GetProductsByCategoryID(categoryID)方法。要向DAL添加一個新的方法,返回DataSet的設計視圖,右擊ProductsTableAdapter部分,選擇“Add Query”。

          圖片14:右擊TableAdapter并且選擇“Add Query”

          圖片14:右擊TableAdapter并且選擇“Add Query”

          我們首先會被詢問是否想要使用一個*******************或者是一個新的或已經存在的存儲過程。我們再次選擇使用ad-hoc SQL***。接下來我們會被詢問我們將要使用哪種類型的SQL查詢。因為我們想返回屬于某種類別的商品,所以我們要選擇一個返回行的SELECT語句。(We are first prompted about whether we want to access the database using an ad-hoc SQL statement or a new or existing stored procedure. Let's choose to use an ad-hoc SQL statement again. Next, we are asked what type of SQL query we'd like to use. Since we want to return all products that belong to a specified category, we want to write a SELECT statement which returns rows.)

          圖片15:選擇一個返回行的SELECT語句

          圖片15:選擇一個返回行的SELECT語句

          下一步是定義一個用來訪問數據的SQL語句。因為我們只想返回某一特定類別的商品,我們將會使用跟GetProducts()方法相同的SELECT語句,但是我們填加下邊的子句:WHERE CategoryID = @CategoryID。這個@CategoryID參數向TableAdapter向導指示了我們建立的這個方法將需要一個對應類型的輸入參數。

          圖片16:輸入返回某一特定類別的商品的查詢語句

          圖片16:輸入返回某一特定類別的商品的查詢語句

          最后我們需要選擇使用哪種數據訪問的模式,并且把產生的方法的名字改成我們想要的。在Fill模式中,我們將名字改為“FillByCategoryID”,在Return a DataTable模式中,我們將名字改為“GetProductsByCategoryID”。

          圖片17:為TableAdapter方法選擇名字

          圖片17:為TableAdapter方法選擇名字

          當向導結束后,在DataSet的設計視圖里包含了一個新的TableAdapter方法。

          圖片18:現在可以通過類別查詢了。

          圖片18:現在可以通過類別查詢了

          通過同樣的方法我們再建立一個GetProductByProductID(productID)方法。這些帶參數的查詢可以在DataSet的設計視圖中被直接的測試。右擊TableAdapter中的方法,并且選擇“Preview Data”。

          圖片19:屬于飲料的商品被顯示出來了

          圖片19:屬于飲料的商品被顯示出來了

          接下來,輸入一個參數值,點擊“Preview”。圖片19:屬于飲料類別的商品被顯示出來有了DAL中的GetProductsByCategoryID(categoryID)方法,我們現在便可以創建一個asp.net頁來顯示屬于某種特殊類別的商品了。下邊的這個例子將顯示所有屬于飲料類別的商品,該CategoryID為1。

          Beverages.aspx

          <%@?Page?Language="C#"?AutoEventWireup="true"?CodeFile="Beverages.aspx.cs"?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>
          ????????????
          &nbsp;</p>
          ????
          </div>
          ????
          </form>
          </body>
          </html>

          Beverages.aspx.cs

          using?System;
          using?System.Data;
          using?System.Configuration;
          using?System.Collections;
          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;
          using?NorthwindTableAdapters;

          public?partial?class?Beverages?:?System.Web.UI.Page
          {
          ????
          protected?void?Page_Load(object?sender,?EventArgs?e)
          ????
          {
          ????????ProductsTableAdapter?productsAdapter?
          =?new
          ?????????ProductsTableAdapter();
          ????????GridView1.DataSource?
          =
          ??????????productsAdapter.GetProductsByCategoryID(
          1);
          ????????GridView1.DataBind();
          ????}

          }

          圖片20:屬于飲料類別的商品被顯示出來

          圖片20:屬于飲料類別的商品被顯示出來

          第四步:插入,更新和刪除數據

          通常有兩種模式可以用來插入、更新和刪除數據。第一種模式,我稱之為直接操作數據庫模式,它包含創建一些方法,當這個方法被執行的時候,將會向數據庫發出一個插入、更新或刪除的命令,這條命令將會作用于單一的一條記錄。這些方法將會傳入一系列的值(integers, strings, Booleans, DateTimes等等),這些值對應到要插入、更新或刪除的值。例如,運用這種模式,對于Products表,刪除的方法將會帶有一個integer類型的參數,用它來指出要刪除的記錄的ProductID,然而,插入的方法中可能會帶有一個代表ProductName的string類型的參數,一個代表UnitPrice的decimale類型的參數和一個代表UnitsOnStock的integer類型的變量,等等。

          圖片21:每一個插入、更新和刪除的請求立即被傳送給數據庫

          圖片21:每一個插入、更新和刪除的請求立即被傳送給數據庫

          另一種模式,我稱之為批量更新模式,這種模式將會在一個方法調用中更新整個的DataSet,DataTable,或者是一個DataRows集合。通過這種模式,一個開發者將會在一個DataTable中刪除、插入或者修改DataRows,然后將這些DataRows或DataTable傳入到一個更新的方法中,這個方法將會計算被傳入的這些DataRows,判斷它們是否被修改了,添加了或刪除了(通過利用DataRows的RowState屬性值),然后對每一條記錄執行適當的數據庫請求。

          圖片22:當Update方法被調用時,所有的變化將會同時作用于數據庫

          圖片22:當Update方法被調用時,所有的變化將會同時作用于數據庫

          TableAdapter默認將會使用批處理方式,但同時也支持直接操作數據庫模式。由于當建立TableAdapter時,在Advanced Properties中,我們選擇了“Generate Insert, Update, and Delete statements”選項,ProductsTableAdapter中包含了一個Update()方法,這個方法將會執行批量更新模式。特別的,TableAdapter包含了一個Update()方法,可以給這個方法傳遞DataSet類,一個強類別的DataTable,或者一個或更多的DataRows。當你第一次建立TableAdapter時選中了GenerateDBDirectMethods的話,直接操作數據庫模式也會通過Insert()、Update()、Delete()方法被執行。

          兩種模式都是通過使用TableAdapter的InsertCommand、UpdateCommand和DeleteCommand的屬性來執行他們的插入、更新和刪除命令的。點擊在DataSet視圖中的TableAdapter然后來到屬性窗口,你可以測試和修改InsertCommand、UpdateCommand和DeleteCommand的屬性。

          圖片23:TableAdapter包含InsertCommand、UpdateCommand和DeleteCommand屬性

          圖片23:TableAdapter包含InsertCommand、UpdateCommand和DeleteCommand屬性

          為了測試和修改這些數據庫命令屬性,點擊CommandText子屬性,將會彈出一個查詢生成器

          圖片24:在查詢生成器中生成Insert、Update和Delete語句

          圖片24:在查詢生成器中生成Insert、Update和Delete語句

          接下來的例子說明了如何使用批處理模式來把所有非discontinued并且units in stock 為25或更少的商品的價格成2倍:

          NorthwindTableAdapters.ProductsTableAdapter?productsAdapter?=
          ??
          new?NorthwindTableAdapters.ProductsTableAdapter();

          //?For?each?product,?double?its?price?if?it?is?not?discontinued?and
          //?there?are?25?items?in?stock?or?less
          Northwind.ProductsDataTable?products?=?productsAdapter.GetProducts();
          foreach?(Northwind.ProductsRow?product?in?products)
          ???
          if?(!product.Discontinued?&&?product.UnitsInStock?&lt;=?25)
          ??????product.UnitPrice?
          *=?2;

          //?Update?the?products
          productsAdapter.Update(products);

          接下來的代碼說明了如何使用直接操作數據庫模式來程式化的刪除一條指定的記錄,然后更新一條,并且添加一條新的記錄:

          NorthwindTableAdapters.ProductsTableAdapter?productsAdapter?=?new?NorthwindTableAdapters.ProductsTableAdapter();

          //?Delete?the?product?with?ProductID?3
          productsAdapter.Delete(3);

          //?Update?Chai?(ProductID?of?1),?setting?the?UnitsOnOrder?to?15
          productsAdapter.Update("Chai",?1,?1,?"10?boxes?x?20?bags",
          ??
          18.0m,?39,?15,?10,?false,?1);

          //?Add?a?new?product
          productsAdapter.Insert("New?Product",?1,?1,
          ??
          "12?tins?per?carton",?14.95m,?15,?0,?10,?false);

          創建客戶化的插入、更新和刪除方法

          通過數據庫直接創建的Insert()、Update()和Delete()方法有的時候會顯得有些笨重,特別市在一個表中包含很多的列的時候。看一下前邊的那些代碼,如果沒有IntelliSense的幫助,對于傳遞給Update()和Insert()方法的每個輸入的參數,我們很難知道這個Products表的列的結構。或許有這樣的幾種情況,我們只想更新一個或兩個列,或者想要客戶化一個Insert()方法,這個方法將會返回新創建的這條記錄的主鍵的值。

          想要創建這樣的一個客戶化的方法,返回DataSet視圖。右擊TableAdapter并且選擇Add Query,返回到TableAdapter向導。在第二個對話框中我們可以表明想要建立的查詢類型。讓我們創建一個方法,該方法會添加一條新的商品記錄,并且返回這條新插入的記錄的ProductID值。因此,選擇創建一個INSERT查詢。

          圖片25:創建一個向Products表中添加一條新的記錄的方法

          圖片25:創建一個向Products表中添加一條新的記錄的方法

          在下一個對話框中,InsertCommand的CommandText將會出現。通過在查詢的最后添加SELECT SCOPE_IDENTITY()來擴充這條查詢語句,這條語句將會返回新插入的記錄中最后一條的主鍵值。要確保你在插入這條SELECT語句之前的INSERT語句要以分號結束。

          圖片26:添加返回SCOPE_IDENTITY()的值的查詢

          圖片26:添加返回SCOPE_IDENTITY()的值的查詢

          最后,將新的方法命名為InsertProduct

          圖片27:將新的方法命名為InsertProduct

          圖片27:將新的方法命名為InsertProduct

          當你返回到DataSet視圖時,你會發現ProductsTableAdapter包含了一個新的方法,InsertProduct。如果在這個方法中沒有為每一個Products表中的列傳遞一個參數的話,很有可能是你沒有以 結束INSERT語句。一定要確保你是以分號結束INSERT語句的。

          按照默認,Insert方法是不會執行任何查詢的,它只能返回起作用的記錄數。然而,我們想要InsertProduct方法返回由查詢返回的值,并不是起作用的列數。要實現這種效果,需要調整InsertProduct方法的ExecuteMode屬性為Scalar。

          圖片28:調整InsertProduct方法的ExecuteMode屬性為Scalar

          圖片28:調整InsertProduct方法的ExecuteMode屬性為Scalar

          下邊的代碼表示了如何使用新建立的InsertProduct方法:

          NorthwindTableAdapters.ProductsTableAdapter?productsAdapter?=?new?NorthwindTableAdapters.ProductsTableAdapter();

          //?Add?a?new?product
          int?new_productID?=?Convert.ToInt32(productsAdapter.InsertProduct("New?Product",?1,?1,?"12?tins?per?carton",?14.95m,?10,?0,?10,?false));

          //?On?second?thought,?delete?the?product
          productsAdapter.Delete(new_productID);

          第五步:完成數據訪問層

          我們注意到,ProductsTableAdapters類從Products表中返回了CategoryID和SupplierID的值,但是不包括Categories表的CategoryName列和Suppliers表的CompanyName列,即使這些列都是當展示商品的時候我們希望顯示出來的。我們可以擴充一下TableAdapter的構造方法,GetProducts(),讓它包含CategoryName和CompanyName這兩個列的值,這個方法也會更新這個強類別的DataTable,使它包含這些新的列。

          這樣將會產生一個問題,這些用來插入、更新和刪除數據的TableAdapter的方法都是基于這個構造方法的。幸運的是,這個自動生成的用來插入、更新和刪除的方法不會被SELECT中的子查詢所影響。通過向我們的查詢語句中添加Categories和Suppliers子查詢,而不使用JOINS,我們可以避免為了修改數據而不得不重新編寫這些方法。在ProducsTableAdapter上右擊GetProducts()方法并且選擇Configure。然后,調整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

          圖片29:為GetProducts()方法更新SELECT語句

          圖片29:為GetProducts()方法更新SELECT語句

          更新了GetProducts()方法后,這個DataTable將會包含兩個新的列:CategoryName和SupplierName。

          圖片30:Products DataTable有兩個新的列

          圖片30:Products DataTable有兩個新的列

          用同樣的方法也更新一下GetProductsByCategoryID(categoryID)方法中的SELECT語句。

          如果你是使用JOINS語句來更新GetProducts()的SELECT語句的話,DataSet視圖將不會自動生成采用數據庫直接模式來插入、更新和刪除數據的方法。你將不得不像前面的教程中我們處理InsertProduct方法那樣手動地建立他們。另外,如果你想使用批處理的更新模式的話,你還需要手動地添加InsertCommand,UpdateCommand和DeleteCommand屬性值。

          添加剩下的tableadapters

          直到現在,我們僅僅是處理單一的數據庫表的單獨的一個TableAdapter。但是,Northwind數據庫包含了許多相關連的表,這些表都是在我們web應用程序中需要用到的。一個DataSet類可以包含多個相關連的DataTables。因此,為了完成我們的數據訪問層,需要添加我們將要使用的其他的表所對應的DataTables。要向一個DataSet類中添加DataTables,打開DataSet視圖,右擊視圖,選擇Add/TableAdapter。這將會建立一個新的DataTable和TableAdapter并且會打開一個向導。

          通過以下的查詢來建立以下的TableAdapters和方法。注意,在ProductsTableAdapter中的查詢包括了獲取每個商品的類別(category)和供貨者名字(supplier name)的子查詢。并且,如果你是跟著教程一步步地做下來的話,你應該已經添加了ProductsTableAdapter類的GetProducts()和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

          圖片31:在四個TableAdapters后邊的DataSet視圖已經被添加進來

          圖片31:在四個TableAdapters后邊的DataSet視圖已經被添加進來

          向數據訪問層添加客戶化的代碼

          向DataSet類中添加的TableAdapter和DataTables是作為一個XML摘要定義文件(Schema Definition file(Norghwind.xsd))被表示的。你可以通過右擊在Solution Explorer中的Norghwind.xsd文件并選擇View Code來查看這個摘要信息。

          圖片32:Northwinds DataSet類的XML摘要定義文件(XSD)

          圖片32:Northwinds DataSet類的XML摘要定義文件(XSD)

          這個摘要信息在編譯或者運行時(如果需要)會被傳遞給在設計階段的C#或Visual Basic代碼,這個時候你就可以一步步地進行調試了。想要查看這個自動生成的代碼,在Class View中點開TableAdapter或DataSet類。如果你看不到Class View的話,可以到View菜單并選種它或者按Ctrl+Shift+C鍵。在類視圖(Class View)中,你可以看到DataSet類和TableAdapter類的屬性、方法和事件。想要查看某個特殊的方法,雙擊在類視圖中這個方法的名或者右擊并選擇Go To Definition。

          圖片33:通過在類視圖中選擇Go To Definition來查看自動生成的代碼

          圖片33:通過在類視圖中選擇Go To Definition來查看自動生成的代碼

          雖然利用自動生成代碼會節省很多時間,但是代碼經常是很籠統并且需要我們進行客戶化來滿足一個應用程序的特殊需求。擴充自動生成代碼的風險在于,生成代碼的這個工具會不定時地重生成代碼并會覆蓋掉你所客戶化的代碼。在.NET2.0的新的部分類思想中,在多個文件中分離出一個類來是很簡單的。這就使我們能夠向自動生成的類中添加我們自己的方法、屬性和事件,而不必擔心Visual Studio會覆蓋我們的客戶化了的代碼。

          為了表明如何客戶化數據訪問層,讓我們向SuppliersRow類中添加一個GetProducts()方法。這個SuppliersRow類代表了Suppliers表中的一條記錄。每個供貨商(supplier)可以提供0到多哥商品(products),所以GetProducts()將會返回特定的供貨商的商品。為了實現這個,在App_Code文件夾中新建一個名為SuppliersRos.cs的類文件并且添加以下的代碼:

          using?System;
          using?System.Data;
          using?NorthwindTableAdapters;

          public?partial?class?Northwind
          {
          ????
          public?partial?class?SuppliersRow
          ????
          {
          ????????
          public?Northwind.ProductsDataTable?GetProducts()
          ????????
          {
          ????????????ProductsTableAdapter?productsAdapter?
          =
          ?????????????
          new?ProductsTableAdapter();
          ????????????
          return
          ??????????????productsAdapter.GetProductsBySupplierID(
          this.SupplierID);
          ????????}

          ????}

          }

          這個類告訴編輯器當構建Norghwind.SuppliersRow類時,向其中添加我們剛剛定義的那個GetProducts()方法。如果你運行你的工程并返回到類視圖的時候,你將會看到GetProducts() 是作為一個Northwind.SuppliersRow方法被列出來的。

          圖片34:GetProducts()方法現在已經成為Northwind.SuppliersRow類的一部分了

          圖片34:GetProducts()方法現在已經成為Northwind.SuppliersRow類的一部分了

          GetProducts()方法現在已經能夠被用來列舉某一特殊的供貨商的商品了,就像下邊的代碼所表示的那樣:

          NorthwindTableAdapters.SuppliersTableAdapter?suppliersAdapter?=?new?NorthwindTableAdapters.SuppliersTableAdapter();

          //?Get?all?of?the?suppliers
          Northwind.SuppliersDataTable?suppliers?=
          ??suppliersAdapter.GetSuppliers();

          //?Enumerate?the?suppliers
          foreach?(Northwind.SuppliersRow?supplier?in?suppliers)
          {
          ????Response.Write(
          "Supplier:?"?+?supplier.CompanyName);
          ????Response.Write(
          "<ul>");

          ????
          //?List?the?products?for?this?supplier
          ????Northwind.ProductsDataTable?products?=?supplier.GetProducts();
          ????
          foreach?(Northwind.ProductsRow?product?in?products)
          ????????Response.Write(
          "<li>"?+?product.ProductName?+?"</li>");

          ????Response.Write(
          "</ul><p>&nbsp;</p>");
          }

          這個數據同樣可以在任何ASP.NET的WEB控件中被顯示。下邊的頁使用了一個包含兩個字段的GridView控件:

          ??? 綁定列,該列顯示了每個供伙商的名字。
          ??? 模板列,該列包含一個BulletedList控件,該控件被綁定到為每個供貨商通過GetProducts()方法返回的結果。

          在以后的教程中,我們將會測驗如何表示詳細的報告。現在,這個例子被設計用來展現向Northwind.SuppliersRow類中添加客戶化的方法。

          SuppliersAndProducts.aspx

          <%@?Page?Language="C#"?AutoEventWireup="true"?CodeFile="SuppliersAndProducts.aspx.cs"?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="<%#
          ??????????????((Northwind.SuppliersRow)((System.Data.DataRowView)
          ??????????????Container.DataItem).Row).GetProducts()?%>"

          ????????????????????????????????????DataTextField
          ="ProductName">
          ????????????????????????????
          </asp:BulletedList>
          ????????????????????????
          </ItemTemplate>
          ????????????????????
          </asp:TemplateField>
          ????????????????
          </Columns>
          ????????????
          </asp:GridView>
          ????????????
          &nbsp;</p>

          ????
          </div>
          ????
          </form>
          </body>
          </html>

          SuppliersAndProducts.aspx.cs

          using?System;
          using?System.Data;
          using?System.Configuration;
          using?System.Collections;
          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;
          using?NorthwindTableAdapters;

          public?partial?class?SuppliersAndProducts?:?System.Web.UI.Page
          {
          ????
          protected?void?Page_Load(object?sender,?EventArgs?e)
          ????
          {
          ????????SuppliersTableAdapter?suppliersAdapter?
          =?new
          ??????????SuppliersTableAdapter();
          ????????GridView1.DataSource?
          =?suppliersAdapter.GetSuppliers();
          ????????GridView1.DataBind();
          ????}

          }

          圖片35:供貨商公司的名字被列在左側的列中,他們的商品在右側

          圖片35:供貨商公司的名字被列在左側的列中,他們的商品在右側

          posted on 2007-03-19 15:20 曾科 閱讀(1575) 評論(0)  編輯  收藏 所屬分類: ASP.NET
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(3)

          隨筆檔案

          文章分類

          文章檔案

          相冊

          .net

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 腾冲县| 偃师市| 屏南县| 怀集县| 禄丰县| 嘉禾县| 山东省| 八宿县| 浮山县| 永城市| 比如县| 大石桥市| 太保市| 景德镇市| 全南县| 湘潭县| 丁青县| 通江县| 云南省| 贵州省| 曲靖市| 九龙县| 济宁市| 涟水县| 莆田市| 平泉县| 永川市| 陵川县| 山阳县| 光泽县| 武定县| 长宁区| 宣威市| 新巴尔虎右旗| 康乐县| 抚顺市| 南雄市| 涿州市| 民权县| 湾仔区| 义乌市|