作為開發(fā)人員,當(dāng)我們在學(xué)習(xí)新技術(shù)時(shí),例子可能是我們最大的敵人。而教程往往設(shè)計(jì)得易于理解,但是同時(shí),它們常常加固了懶惰,低效性,甚至于危險(xiǎn)的編碼習(xí)慣。再也沒有比ADO.NET示例更能說明問題的了。在本文中,我們將準(zhǔn)備分析一下強(qiáng)類型對象對于你的數(shù)據(jù)庫開發(fā)的意義以及為什么在沒有例子的情況下你應(yīng)該在應(yīng)用程序中盡量使用強(qiáng)類型對象。
具體地說,我們將分析怎樣在Visual Studio 2005中創(chuàng)建和使用強(qiáng)類型DataSet。正如本文所探討的,相對于其它可選的弱類型化的數(shù)據(jù)存取技術(shù),強(qiáng)類型DataSet提供了很多優(yōu)點(diǎn);并且,借助于Visual Studio 2005,創(chuàng)建和使用強(qiáng)類型DataSet比以往更為容易。
二、 強(qiáng)類型對象基礎(chǔ)及優(yōu)點(diǎn)
為了理解強(qiáng)類型化的含義,不妨讓我們先考慮一下約會的例子。如果你是一個單身漢,那么,你盼望與什么類型的人約會呢?你可能已經(jīng)有了自己具體的標(biāo)準(zhǔn),如富裕且吸引人或可能是居住條件優(yōu)越而性感。無論你的條件如何,當(dāng)你決定想與誰在一起呆更長的時(shí)間時(shí),你都難免有一套自己的約會標(biāo)準(zhǔn)。如果你很明智,可以列出一個經(jīng)過事先深思熟慮的列表,這樣可以幫助你節(jié)約不必要的感情付出。把一項(xiàng)"非酗酒"的條件加入到你的約會標(biāo)準(zhǔn)中將會節(jié)約你大量的時(shí)間,并且允許你把你的時(shí)間和精力用于與更好的候選者約會。
你可能疑惑,這怎么能與編程進(jìn)行比較呢?請聽我的解釋。ADO.NET數(shù)據(jù)存取對象的設(shè)計(jì)是為了實(shí)現(xiàn)最大的靈活性。除非你遇到相當(dāng)大的麻煩,否則,當(dāng)你從數(shù)據(jù)庫讀取數(shù)據(jù)時(shí),你都會使用大量普通的未經(jīng)類型化的對象-當(dāng)然,.NET框架完全允許這樣做。而使用我們的約會類比法,總是把你的關(guān)系數(shù)據(jù)當(dāng)作泛型對象則有點(diǎn)象承認(rèn)"我只與滿足我條件的人約會"。難道你不能稍微放寬一些條件嗎?作為你的朋友,我必須建議你"先確定一些標(biāo)準(zhǔn),再精簡一下你的列表!"也許更好些。
正如忽視篩選你要約會的人能夠?qū)е挛磥淼年P(guān)系問題,與你的對象保持"松耦合"能夠給你的代碼帶來錯誤。另外,因?yàn)槿绻阕屓魏闻f對象"出入"你的子程序的話,那么,直到你的應(yīng)用程序在運(yùn)行時(shí)刻執(zhí)行時(shí),你可能才會知道存在問題。在我們的日期約會類比中,在運(yùn)行時(shí)刻捕獲錯誤很類似于你與你的約會者在一家時(shí)髦的意大利餐館進(jìn)行一場痛苦而尷尬的討論。是的,你發(fā)現(xiàn)了這個問題;但是,如果你已經(jīng)事先計(jì)劃好的話,那么你的結(jié)果就不會是"一群用餐者盯著你,而你滿身是意大利烤碎肉卷"。如果你簡單地把一些更緊密的標(biāo)準(zhǔn)應(yīng)用于你的代碼中,那么在你的應(yīng)用程序開始運(yùn)行前(在編譯時(shí)刻)你就能夠捕獲錯誤。例如,請考慮下面的示例代碼:
上面的DataRow就是非類型化的;結(jié)果,你必須以一個串形式作為你要查詢的列名來存取這個值(或者,使用這個列集合中的列的索引值)。很可能,這個列真正存在。一個DataRow列的數(shù)據(jù)類型是對象;我們假定FirstName列的基本數(shù)據(jù)類型是字符串,但是,我們必須顯式地把它轉(zhuǎn)化成一個字符串以便使用它。如果列名發(fā)生變化(例如,你把它改為PersonFirstName),那么編譯器是不能通知你的。千萬不要這樣!因此,如果你的代碼看起來更象如下形式,那么你的生活就會更容易些而你的代碼將更為可靠:
在第二個例子中,我們擁有一個強(qiáng)類型行,并且我們知道FirstName屬性是字符串類型。在此,不會出現(xiàn)雜亂的列名,而且也不存在雜亂的對象轉(zhuǎn)換問題。編譯器為我們作類型檢查,并且我們可以繼續(xù)進(jìn)行其它任務(wù)而不必?fù)?dān)心是否我們已經(jīng)正確輸入了列名。
對于所有其它列也是如此;總之,當(dāng)你能夠使用一個更為具體的類型時(shí),你永遠(yuǎn)不應(yīng)該使用一個泛型對象。但是,請等一下。該強(qiáng)類型對象出自何處?我想我能夠告訴你這些對象是為你自動創(chuàng)建的。然而,正如要建立良好的關(guān)系需要時(shí)間和精力一樣,強(qiáng)類型化你的對象也需要付出其它努力。好的方面在于,這里所花費(fèi)的額外時(shí)間是值得的,而且節(jié)省了將來更多的花在調(diào)試上的時(shí)間。
存在若干種可以實(shí)現(xiàn)強(qiáng)類型化的方法,在本文余下的部分中,我們將討論怎樣在Visual Studio 2005中創(chuàng)建強(qiáng)類型DataSet;當(dāng)然,還將分析一下這樣做的優(yōu)點(diǎn)和缺點(diǎn)。
三、 在VS 2005中創(chuàng)建強(qiáng)類型DataSet
其實(shí),強(qiáng)類型DataSet是一些提前定義了它們自己的列與表的泛型DataSet,這樣編譯器已經(jīng)知道它們將會包含什么內(nèi)容。不是把你的數(shù)據(jù)包裝為一個"露指手套",一個強(qiáng)類型DataSet恰似一個"手套"。每一個Visual Studio的后續(xù)版本會使得強(qiáng)類型化一個DataSet的過程更為容易。在這個示例中,我們將使用來自于SQL Server 2005中的AdventureWorks數(shù)據(jù)庫。這只要簡單地執(zhí)行如下步驟:
1. 打開Visual Studio,然后創(chuàng)建一個新的ASP.NET網(wǎng)站。
2. 在Solution Explorer中,占擊以添加一個新項(xiàng)并且選擇DataSet,并命名為AdventureWorks.xsd。Visual Studio推薦把這個DataSet文件放到App_Code文件夾下。
3. 這個AdventureWorks.xsd將在設(shè)計(jì)模式下打開,并且激活"TableAdapter Configuration"向?qū)А,F(xiàn)在,僅僅點(diǎn)擊一下"Cancel"即可。
4. 定位到Server Explorer工具箱,導(dǎo)航到你的SQL Server 2005數(shù)據(jù)庫和AdventureWorks數(shù)據(jù)庫。(如果你還沒有安裝AdventureWorks數(shù)據(jù)庫的話,你可以從微軟的SQL Server 2005 Samples and Sample Databases下載頁面下載它,還有另外的SQL Server 2005示例。)
5. 把SalesOrderHeader和SalesOrderDetail表拖動到你的DataSet設(shè)計(jì)器窗口。現(xiàn)在,這個窗口應(yīng)該類似于下面的屏幕快照。注意,對于我們加入的每個表,Visual Studio都創(chuàng)建一個強(qiáng)類型DataTable(該名稱是基于原始的表)和一個TableAdapter。這個DataTable為我們定義了每一個列。這個表適配器是我們用來填充這個表的對象。缺省情況下,我們有一個Fill()方法,由它找到表中的每一行。
這個強(qiáng)類型DataSet將返回在這兩個表中的所有記錄。既然AdventureWorks數(shù)據(jù)庫包含大量的訂單信息,那么我們?yōu)槭裁床粍?chuàng)建一個更為具體些的查詢呢?我們可以把方法添加到我們的TableAdapter對象來檢索一個更為具體的表的記錄子集。首先,右擊SalesOrderHeaderTableAdapter,并且選擇"Add|Query"。選擇"Use SQL statements"并且點(diǎn)擊Next按鈕。然后,選擇"SELECT which returns rows"并且點(diǎn)擊Next按鈕。最后,在窗口中輸入下列查詢(或使用Query Builder來實(shí)現(xiàn)相同的任務(wù)):
SalesOrderID, RevisionNumber, OrderDate, DueDate, ShipDate,
Status, OnlineOrderFlag, SalesOrderNumber, PurchaseOrderNumber,
AccountNumber, CustomerID, ContactID, SalesPersonID, TerritoryID,
BillToAddressID, ShipToAddressID, ShipMethodID, CreditCardID,
CreditCardApprovalCode, CurrencyRateID, SubTotal, TaxAmt, Freight,
TotalDue, Comment, rowguid, ModifiedDate
FROM Sales.SalesOrderHeader
WHERE (OrderDate > @OrderDate)
這個SQL查詢是一個簡單的SELECT查詢,它有一個@OrderDate參數(shù)以進(jìn)一步縮小結(jié)果范圍。這樣可以防止我們返回數(shù)據(jù)庫的每一個訂單。點(diǎn)選"Fill a DataTable"和"Return a DataTable"復(fù)選框,然后點(diǎn)擊Finish。在添加這個SELECT語句后,你的設(shè)計(jì)者現(xiàn)在應(yīng)該有一個外部查詢已經(jīng)添加到這個SalesOrderHeaderTableAdapter,請參考下面的屏幕快照。
四、 在一個ASP.NET頁面上使用強(qiáng)類型DataSet
通過創(chuàng)建強(qiáng)類型DataSet,我們可以僅通過編寫幾行代碼就可以實(shí)現(xiàn)容易地在一個ASP.NET頁面顯示這些數(shù)據(jù)。首先,在你的網(wǎng)站上創(chuàng)建一個ASP.NET頁面并且在設(shè)計(jì)模式中觀察它。然后,拖放一個GridView控件到其上,保持其ID為GridView1。打開ASP.NET頁面的源代碼并且在文件的頂部導(dǎo)入AdventureWorksTableAdapters命名空間(在C#中,該語法為:"using AdventureWorksTableAdapters;")。最后,把下列代碼添加到Page_Load事件處理器中:
SalesOrderHeaderTableAdapter salesAdapter =
new SalesOrderHeaderTableAdapter();
//得到發(fā)生于2004年7月1日之后的訂單
AdventureWorks.SalesOrderHeaderDataTable Orders =
salesAdapter.GetDataBy(new DateTime(2004, 7, 1));
//把訂單結(jié)果綁定到GridView
this.GridView1.DataSource = Orders;
this.GridView1.DataBind();
這段代碼是很簡單的。我們創(chuàng)建SalesOrderHeaderTableAdapter的一個實(shí)例-我們將使用它來填充DataTable。注意,不是聲明一個泛型DataTable,我們聲明了一個SalesOrderHeaderDataTable類型的對象。為了填充這個DataTable,我們調(diào)用GetDateBy()方法并且傳遞給它一個DateTime對象。還要注意,甚至這個檢索命令也是強(qiáng)類型化的,因?yàn)槲覀儽仨殏鬟f一個DateTime對象,而不僅僅是一個泛型對象。下面的屏幕快照顯示了上面示例代碼的清晰結(jié)果。
除了通過代碼把結(jié)果綁定到GridView外,你還可以使用一個ObjectDataSource,設(shè)置它的TypeName屬性為AdventureWorksTableAdapters.SalesOrderHeaderTableAdapter,并且把它的SelectMethod設(shè)置為GetData或GetDataBy。
五、 使用強(qiáng)類型DataSet插入、更新和刪除數(shù)據(jù)
在本文中,我們已經(jīng)看到了怎樣使用一個強(qiáng)類型DataSet從一個數(shù)據(jù)庫中選擇數(shù)據(jù)。然而,你還可以使用這些工具來插入、更新和刪除基本的數(shù)據(jù)庫數(shù)據(jù)。
除了不必編寫代碼來存取數(shù)據(jù)庫外,使用這個強(qiáng)類型化的DataSet的另外一個很大的優(yōu)點(diǎn)是,在此不存在編譯器不能檢查的字符串列名潛伏在我們的代碼并且我們不必進(jìn)行任何對象轉(zhuǎn)換。如果我們曾經(jīng)改變過我們的數(shù)據(jù)庫模式,那么一旦我們更新我們的AdventureWorks.xsd文件,我們將注意到在編譯期間的存在于我們的應(yīng)用程序中的所有的巨大變化。
六、 小結(jié)
其實(shí),除了使用強(qiáng)類型DataSet外,還有另外的方法來實(shí)現(xiàn)你的應(yīng)用程序的強(qiáng)類型化。你可以創(chuàng)建定制類-比DataSet更為輕量級并且能夠正確地響應(yīng)于你的數(shù)據(jù)庫。而且,還有一些第三方軟件開發(fā)者提供工具來自動化這一過程。其中,一個特別的產(chǎn)品是LLBLGen Pro,就是我比較喜歡的工具之一,我還寫了有關(guān)于它的一本書:《Rapid C# Windows Development: Visual Studio 2005, SQL Server 2005,and LLBLGen Pro》。另外一個流行的工具是CodeSmith。甚至微軟也在使用一個與之類似的工具-DLINQ,它仍處于測試階段,至少在下一年中不會上市。
如果你使用Visual Studio強(qiáng)類型DataSet方法,那么你不需要購買任何另外的軟件-這是一個明顯的優(yōu)點(diǎn)。所有這些解決方案都有其各自不同的特征和優(yōu)點(diǎn),但是強(qiáng)類型化你的關(guān)系數(shù)據(jù)的主要優(yōu)點(diǎn)還在于:可靠性,更少的錯誤和更少的調(diào)試時(shí)間花費(fèi);另外,分析數(shù)據(jù)庫模式變化的影響并實(shí)現(xiàn)它們也更為容易。最后,非常希望你已經(jīng)了解到強(qiáng)類型化的優(yōu)點(diǎn)。祝你幸運(yùn)!