整個(gè)指南系列將從最簡單的內(nèi)容開始,我們會(huì)首先創(chuàng)建一個(gè)數(shù)據(jù)訪問層(DAL),在DAL中通過強(qiáng)類型數(shù)據(jù)集(Typed DataSet)訪問數(shù)據(jù)庫中的信息。
一、入門(introduction)
作為Web開發(fā)者,我們會(huì)一直不斷的與數(shù)據(jù)打交道,比如:創(chuàng)建數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù);編寫代碼訪問和修改數(shù)據(jù);通過Web頁面收集和匯總數(shù)據(jù)等等。
這個(gè)相對(duì)來說比較長的指南系列將會(huì)揭示asp.net2.0數(shù)據(jù)操作的通用模式。我們將從創(chuàng)建整個(gè)系列的軟件架構(gòu)開始。整個(gè)架構(gòu)將包括DAL(通過強(qiáng)類型數(shù)據(jù)集實(shí)現(xiàn)),BLL(展示業(yè)務(wù)邏輯規(guī)則)和UI(由asp.net頁面組成,而這些頁面又都共享使用一個(gè)模板來布局)。等這些基礎(chǔ)工作完成以后,將開始展示數(shù)據(jù),包括如何顯示、匯總、收集和驗(yàn)證數(shù)據(jù)。
為了讓您了解的更加清楚,整個(gè)系列將會(huì)遵循簡單明了和通過大量截圖分布演示的風(fēng)格來展現(xiàn)。每一篇文章都是用C#和VB.NET兩種語言編寫代碼,你可以下載到相應(yīng)語言的源代碼。(作者注:第一篇文章非常長,后面的就按內(nèi)容分塊,相對(duì)更好理解一些)
在整個(gè)系列中,我們將會(huì)使用SQL SERVER 2005 Exdivss Edition中的示例數(shù)據(jù)庫Northwind,它會(huì)被放在項(xiàng)目的App_Date文件夾中,除了數(shù)據(jù)庫文件外,文件夾還包括創(chuàng)建數(shù)據(jù)庫用的SQL腳本,為您使用不同的數(shù)據(jù)庫版本提供了方便(如果你愿意的話),這些SQL腳本可以在微軟的官方網(wǎng)站下載。需要說明的是,如果您選擇使用了一個(gè)不同的數(shù)據(jù)庫版本,需要修改Web.config文件中的NORTHWNDConnectionString的內(nèi)容。整個(gè)Web應(yīng)用程序通過VS2005專業(yè)版創(chuàng)建,項(xiàng)目類型是一個(gè)基于文件系統(tǒng)的Web站點(diǎn)項(xiàng)目。當(dāng)然,系列中的所有內(nèi)容,都可以非常好地運(yùn)行在其它免費(fèi)地VS2005版本或者Visual Web Developer中。
在“入門”章節(jié)中,首先創(chuàng)建DAL,接著第二篇文章創(chuàng)建BLL,第三篇開始頁面布局和導(dǎo)航。前三篇文章將建立整個(gè)系列地基礎(chǔ)平臺(tái)。第一篇文章內(nèi)容會(huì)稍微多一些,現(xiàn)在讓我們打開VS開始上路吧!
第1步:創(chuàng)建Web項(xiàng)目并連接數(shù)據(jù)庫
在我們建立DAL之前,首先創(chuàng)建一個(gè)站點(diǎn),并且安裝數(shù)據(jù)庫,下面創(chuàng)建一個(gè)新的基于文件系統(tǒng)的ASP.NET站點(diǎn)。操作如下:打開VS的“文件”/“新建網(wǎng)站”菜單,顯示創(chuàng)建新網(wǎng)站對(duì)話框,選擇“asp.net web site”模板,在位置(location)下來列表中,選擇“文件系統(tǒng)”,選擇一個(gè)文件夾存放Web站點(diǎn),同時(shí)將語言改成C#.
圖1 創(chuàng)建新網(wǎng)站
上述步驟將創(chuàng)建一個(gè)新的Web站點(diǎn),包括一個(gè)Default.aspx頁面,一個(gè)App_Data文件夾和一個(gè)Web.config文件。
下一步是通過VS SERVER EXPLORER添加對(duì)數(shù)據(jù)庫的引用,通過往SERVER EXPLORE中添加數(shù)據(jù)庫,您可以添加表、存儲(chǔ)過程、視圖等等。您可以查看表中的數(shù)據(jù),也可以手動(dòng)或通過QUERY BUILDER來創(chuàng)建查詢,另外,我們可以指定用于創(chuàng)建強(qiáng)類型數(shù)據(jù)集的數(shù)據(jù)庫。在我們提供數(shù)據(jù)庫連接字符串信息的同時(shí),VS會(huì)自動(dòng)裝載一個(gè)下拉列表用來展示已經(jīng)在SERVER EXPLORE注冊(cè)的數(shù)據(jù)庫。
添加northwind數(shù)據(jù)庫的具體步驟取決于,你是想通過App_Data文件夾中的SQL SERVER 2005 EXdivSS EDITION,還是想用SQL SERVER 2000或SQL SERVER 2005的數(shù)據(jù)庫。
1.1使用App_Data中的數(shù)據(jù)庫
如果您沒有SQL SERVER 2000或2005的數(shù)據(jù)庫服務(wù)器,或者說您不想往數(shù)據(jù)庫服務(wù)器中添加數(shù)據(jù)庫,您可以用SQL SERVER 2005 EXdivSS EDITION版的northwind數(shù)據(jù)庫,您可以通過下載本項(xiàng)目的源代碼,然后在App_Data中找到它(NorthWND.MDF).
位于App_Data文件夾下的數(shù)據(jù)庫會(huì)自動(dòng)添加到SERVER EXPLORE中,加入您的機(jī)器已經(jīng)安裝了SQL SERVER 2005 EXdivSS EDITION,您會(huì)在SERVER EXPLORE中看到一個(gè)northwnd.mdf的節(jié)點(diǎn)。您可以展開并查看其中的表、視圖、存儲(chǔ)過程等。
在App_Data文件夾中,您同樣可以放置MICROSOFT ACCESS的.mdb文件,它同樣也會(huì)被自動(dòng)添加到SERVER EXPLORE中,如果您不想使用任何的SQL SERVER版本,可以下載一個(gè)MICROSOFT ACCESS版本的數(shù)據(jù)庫,并把它放在App_Data中。但是注意:ACCESS數(shù)據(jù)庫相對(duì)于SQL SERVER沒有豐富的特性,而且其不是專為Web站點(diǎn)的開發(fā)設(shè)計(jì)的,另外,后續(xù)的至少35篇文章中使用的特性可能ACCESS會(huì)不支持。
1.2 創(chuàng)建到SQL SERVER 2000或2005的數(shù)據(jù)庫連接
作為另一種方案,您也可以連接已經(jīng)安裝在數(shù)據(jù)庫服務(wù)器中的northwind數(shù)據(jù)庫,前提是數(shù)據(jù)庫已經(jīng)安裝到服務(wù)器了。如果沒有,您可以通過下載本示例教程中的安裝腳本或者從微軟的網(wǎng)站下載SQL SERVER 2000版的northwind和安裝腳本。
如果您已經(jīng)安裝了northwind,打開VS中的SERVER EXPLORE,右擊“Data Connections”節(jié)點(diǎn),選擇“添加連接”。如果您沒有打開SERVER EXPLORE,可以通過“視圖/服務(wù)器管理器”或按“ALT+CTRL+S”快捷鍵打開。打開“添加連接”對(duì)話框后,指定要連接的目標(biāo)服務(wù)器,授權(quán)信息和數(shù)據(jù)庫,如果您已經(jīng)成功配置了數(shù)據(jù)庫連接信息,單擊“確定”按鈕,數(shù)據(jù)庫就會(huì)作為“Data Connections”節(jié)點(diǎn)下一個(gè)新節(jié)點(diǎn)添加到SERVER EXPLORE,您同樣可以展開,并查看其中的表、視圖、存儲(chǔ)過程等。
圖2 添加連接
第2步:創(chuàng)建DAL
處理數(shù)據(jù)的一個(gè)選擇是將處理的詳細(xì)邏輯直接放在顯示層(在Web應(yīng)用程序中,ASP.NET頁面組成了顯示層),這種形式,通過將ADO.NET寫到ASP.NET頁面下的編碼區(qū)域或者使用SQLDATASOURCE控件。這種途徑將DAL和顯示層緊密的耦合在一起。推薦的一種途徑是將DAL從顯示層分離出來。這個(gè)分離出來的層被叫做數(shù)據(jù)訪問層,往往使用一個(gè)單獨(dú)的類庫項(xiàng)目來實(shí)現(xiàn)。這種架構(gòu)的優(yōu)點(diǎn)有很多,而且有許多說明文檔。
所有數(shù)據(jù)底層數(shù)據(jù)源的代碼(如創(chuàng)建連接,進(jìn)行SELECT,INSERT,UPDATE,DELETE操作)都被放到DAL中,在顯示層不能包括任何數(shù)據(jù)訪問的代碼,可以通過調(diào)用DAL來訪問和請(qǐng)求數(shù)據(jù)。
DAL封裝了訪問底層數(shù)據(jù)的典型方法,例如:northwind數(shù)據(jù)庫中有Products和Categories兩張表,分別記錄了銷售的商品和這些商品的類型。在我們的DAL中,可能要?jiǎng)?chuàng)建如下方法:
GetCategories();返回所有類型的信息。
GetProducts();返回所有商品的信息。
GetProductByCategoryID(CategoryID);返回特定類型的商品。
GetProductByProductID(productid);返回某個(gè)商品的信息。
這些方法執(zhí)行時(shí),會(huì)連接數(shù)據(jù)庫,實(shí)施查詢,返回結(jié)果。如何返回這些結(jié)果是至關(guān)重要的。上述方法可以簡單的返回一個(gè)dataset或datareader,但更完美的一種方式是:返回結(jié)果作為強(qiáng)類型對(duì)象返回。所謂強(qiáng)類型對(duì)象就是在編譯時(shí)就對(duì)其架構(gòu)進(jìn)行了嚴(yán)格的定義,弱類型恰恰想法,只能在運(yùn)行時(shí),才被定義。
例如DataReader和默認(rèn)的DataSet都是弱類型,只能靠數(shù)據(jù)查詢返回的列來定義其架構(gòu)。訪問一個(gè)弱類型的DataTable某一列的方法如下:DataTable.Rows(index)["columnName"].展示了必須通過一個(gè)字符串或下標(biāo)去訪問表中的一列。一個(gè)強(qiáng)類型的DataTable可以將表中的每一列實(shí)現(xiàn)為屬性,當(dāng)然,訪問方式也發(fā)生了變化:DataTable.Rows(index).columnName.
為了返回一個(gè)強(qiáng)類型的對(duì)象,開發(fā)者一是可以創(chuàng)建自定義的業(yè)務(wù)對(duì)象或者可以使用強(qiáng)類型的數(shù)據(jù)集。對(duì)于自定義業(yè)務(wù)對(duì)象,開發(fā)者往往通過定義一個(gè)類來實(shí)現(xiàn),其中類的屬性與底層數(shù)據(jù)庫相應(yīng)表中的列是一一對(duì)應(yīng)的。
一個(gè)強(qiáng)類型的DataSet是VS根據(jù)數(shù)據(jù)庫的架構(gòu)自動(dòng)產(chǎn)生的。其里面的成員都是強(qiáng)類型的。強(qiáng)類型數(shù)據(jù)集中包含的類有效地?cái)U(kuò)展了ADO.NET中的DataSet,DataTable,DataRow。除了強(qiáng)類型的DataTable,強(qiáng)類型DataSet中還包括TableAdapter,這些類中包含了操作DataSet中的表和將表中的改變提交到后臺(tái)數(shù)據(jù)庫的一系列的方法。
我們?cè)谡麄€(gè)系列中將使用強(qiáng)類型數(shù)據(jù)集。下圖展示了使用強(qiáng)類型數(shù)據(jù)集的應(yīng)用程序?qū)优c層之間通信的工作流。
圖3 工作流
2.1 創(chuàng)建強(qiáng)類型數(shù)據(jù)集和TableAdapter
下面開始創(chuàng)建DAL,首先添加一個(gè)強(qiáng)類型的數(shù)據(jù)集到項(xiàng)目中。步驟:右擊項(xiàng)目節(jié)點(diǎn),選擇“添加新項(xiàng)”,選擇DataSet模板,改名為northwind.xsd。
圖4 添加強(qiáng)類型數(shù)據(jù)集
然后,單擊“添加”,當(dāng)提示是否將DataSet添加到App_Code文件夾時(shí),選擇“是”。這是,將出現(xiàn)強(qiáng)類型數(shù)據(jù)集設(shè)計(jì)器,同時(shí)TableAdapter配置向?qū)?huì)啟動(dòng),允許您添加第一個(gè)TableAdapter到DataSet。
強(qiáng)類型數(shù)據(jù)集是一個(gè)強(qiáng)類型數(shù)據(jù)的集合,由多個(gè)強(qiáng)類型DataTable的實(shí)例構(gòu)成,每個(gè)強(qiáng)類型的DataTable由多個(gè)強(qiáng)類型的DataRow實(shí)例構(gòu)成。我們將會(huì)為在項(xiàng)目中用到的底層數(shù)據(jù)庫中的每個(gè)表創(chuàng)建一個(gè)相應(yīng)的強(qiáng)類型DataTable。
下面是為Products表創(chuàng)建一個(gè)DataSet中的強(qiáng)類型表。
請(qǐng)牢記,強(qiáng)類型DataTables不包括訪問底層數(shù)據(jù)庫表的任何信息,為了獲取數(shù)據(jù)填充這個(gè)表,我們用一個(gè)TableAdapter類來充當(dāng)DAL,對(duì)Products表而言,TableAdapter類將包含以下方法(GetProducts(),GetProductsByCategoryId(categoryid)等)。我們可以在顯示層調(diào)用這些方法,DataTable在層與層的數(shù)據(jù)傳遞過程中充當(dāng)強(qiáng)類型的對(duì)象。
TableAdapter配置向?qū)紫茸屇x擇要操作的數(shù)據(jù)庫,在下拉列表中展示了SERVER EXPLORE中的所有的數(shù)據(jù)庫,如果沒有數(shù)據(jù)庫,您可以單擊“新建連接”,添加。
選擇完數(shù)據(jù)庫后,單擊下一步,您要選擇是否保存連接到Web.config文件,如果保存連接字符串,那么就不用在TableAdapter類中硬編碼了。這樣也就避免了一旦將來連接字符串發(fā)生改變帶來的不必要的麻煩。
如果選擇保存到配置文件中,會(huì)保存在相應(yīng)的<connectionstring>節(jié),您同時(shí)可以對(duì)連接字符串進(jìn)行加密,以提供其安全性。也可以通過IIS的圖形化管理工具中的asp.net2.0的屬性頁面來修改。(這在某種程度,對(duì)管理員是一件好事)。
下一步,我們開始定義第一個(gè)強(qiáng)類型DataTable的架構(gòu),而且定義第一個(gè)方法以供TableAdapter來操作強(qiáng)類型DataSet。這兩個(gè)操作,可以通過創(chuàng)建一個(gè)查詢,讓其返回DataTable相應(yīng)的列來實(shí)現(xiàn),而且是同步實(shí)現(xiàn)的。在向?qū)У淖詈螅覀円o查詢命名一個(gè)方法名。當(dāng)所有的這些都完成后,就可以通過顯示層來調(diào)用方法了。這個(gè)方法將會(huì)執(zhí)行定義的查詢形成一個(gè)DataTable。
在定義查詢之前,首先要知道TableAdapter怎樣執(zhí)行查詢,我們可以使用單純的SQL語句,也可以創(chuàng)建一個(gè)新的存儲(chǔ)過程,或者使用一個(gè)現(xiàn)有的存儲(chǔ)過程。在我們的這個(gè)系列中,將使用單純的SQL語句。
那么我們就可以手工輸入SQL語句了,通過創(chuàng)建第一個(gè)方法,讓查詢返回相應(yīng)表中的列,下圖展示了創(chuàng)建一個(gè)查詢返回Products表中所有的行和列。
當(dāng)然,也可以使用查詢生成器,可視化創(chuàng)建查詢。
創(chuàng)建完查詢后,在轉(zhuǎn)到下一頁面前,單擊“高級(jí)”按鈕,在WEB SITE項(xiàng)目中,“Generate Insert,update,and delete statement”是唯一的默認(rèn)選中項(xiàng);如果您的項(xiàng)目是類庫或者是Windows項(xiàng)目,“use optimistic concurrency”選項(xiàng)也是被選中的,現(xiàn)在請(qǐng)不要選擇這個(gè)復(fù)選框,對(duì)于這一話題,將在以后的文章中探討。
配置完高級(jí)選項(xiàng)后,單擊“下一步”到最后一個(gè)頁面,我們將選擇哪個(gè)方法添加到TableAdapter中,有兩種方式:
填充一個(gè)表:創(chuàng)建一個(gè)方法,以查詢結(jié)果生成的DataTable作為參數(shù),并對(duì)其進(jìn)行處理。例如:ADO.NET TableAdapter類調(diào)用Fill()方法。
返回一個(gè)表:方法用來創(chuàng)建、填充表,表作為方法的返回值。
您可以讓TableAdapter實(shí)現(xiàn)其中一個(gè)或兩個(gè)方法都實(shí)現(xiàn),您也可以給方法改名。這里我們兩個(gè)選項(xiàng)全選,但后續(xù)的文章中將使用后一種方法。同時(shí)將通用的GetData方法名改為GetProducts()。
如果選中了最后一個(gè)checkbox,“GenerateDbDirectMethod”將會(huì)為TableAdapter創(chuàng)建Insert,Update和Delete方法。
如果不選,所有的更新將通過TableAdapter唯一的方法Update來實(shí)施。而唯一的這個(gè)方法,對(duì)強(qiáng)類型的Dataset,DataTable,DataRow或者行組都是通用的。
單擊“完成”完成向?qū)?,這時(shí)我們?cè)谠O(shè)計(jì)器中得到一張表??梢钥吹奖碇械乃械牧校≒roductId,ProductName等),同時(shí)還有ProductTableAdapter的所有方法(Fill方法和GetProducts方法)。
至此為止,我們創(chuàng)建了一個(gè)強(qiáng)類型數(shù)據(jù)集和一個(gè)表,和一個(gè)強(qiáng)類型的DataAdapter類。這些對(duì)象可以用來訪問商品列表,代碼如下:
NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
new NorthwindTableAdapters.ProductsTableAdapter();
Northwind.ProductsDataTable products;
products = productsAdapter.GetProducts();
foreach (Northwind.ProductsRow productRow in products)
Response.Write("Product: " + productRow.ProductName + "<br />");
我們不需要寫任何代碼,也不需要實(shí)例化任何ADO.NET類,不需要去管連接字符串,SQL查詢和存儲(chǔ)過程。TableAdapter會(huì)為我們自動(dòng)生成這些底層的代碼。
本例中的每一個(gè)對(duì)象都是強(qiáng)類型的,允許VS提供智能感知和編譯時(shí)的類型判斷。大多數(shù)的TableAdapter返回的表都可以綁定到Web數(shù)據(jù)控件(GridView等)。下面的代碼演示了如何將GetProducts方法返回的結(jié)果綁定到一個(gè)GridView。
AllProducts.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AllProducts.aspx.cs"
Inherits="AllProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"