7. ADOX可以幫你得到并改變Schema信息 ADO.NET并沒(méi)有為得到并管理schema信息提供一個(gè)完全的對(duì)象模式。你應(yīng)該用ActiveX Data Objects Extensions for Data Definition Language and Security (ADOX)或用每個(gè)數(shù)據(jù)庫(kù)提供的本地功能來(lái)得到并改變Schema信息。ADOX是ADO對(duì)象的一個(gè)擴(kuò)展,它包括用來(lái)創(chuàng)建和修改Schema的對(duì)象。你可以編寫適用于各種數(shù)據(jù)源的代碼(不管本地語(yǔ)法有什么不同),因?yàn)锳DOX是管理schema的一個(gè)基于對(duì)象的方法。
你可以用一個(gè)data reader對(duì)象來(lái)讀(不是設(shè)置)簡(jiǎn)單的schema信息。所有的data reader類(OleDbDataReader、SqlDataReader、OracleDataReader)都提供了一個(gè)GetSchemaTable方法,該方法可以讀取查詢到的列的元數(shù)據(jù)信息。GetSchemaTable返回一個(gè)DataTable對(duì)象(格式是每列一行)和固定的一組包含信息的列。返回的元數(shù)據(jù)可以分成三類:列元數(shù)據(jù)、數(shù)據(jù)庫(kù)特征和列屬性。返回的列可以是AllowDBNull、IsAutoIncrement、ColumnName、IsExpression、IsReadonly和NumericPrecision等。在MSDN資料中有完整的列表(見(jiàn)附加資源)。
在調(diào)用ExecuteReader時(shí),如果你執(zhí)行KeyInfo命令,那么GetSchemaTable方法就可以返回更精確的數(shù)據(jù)。你可以將KeyInfo行為同缺省的行為結(jié)合起來(lái),執(zhí)行一個(gè)單獨(dú)的命令并得到schema和數(shù)據(jù):reader = cmd.ExecuteReader( _ CommandBehavior.KeyInfo Or _ CommandBehavior.CloseConnection)
只有執(zhí)行KeyInfo,IsKey、BaseTableName、IsAliased、IsExpression和IsHidden字段的值才能被正確返回。如果執(zhí)行KeyInfo,關(guān)鍵的列(如果有)通常是添加在結(jié)果集的底部的,但不給它們返回?cái)?shù)據(jù)。
8. 用一個(gè)派生的類和自定義的串行化來(lái)節(jié)省空間 只有兩個(gè)ADO.NET對(duì)象是被標(biāo)記為可串行化的——DataTable和DataSet。.NET Framework中的串行化是通過(guò)formatter對(duì)象來(lái)完成的,它們可以將一個(gè)對(duì)象實(shí)例保存到一個(gè)二進(jìn)制或一個(gè)SOAP流(stream)中。.NET formatter用Reflection來(lái)提取任何必要的信息。然而,如果這個(gè)類實(shí)現(xiàn)了ISerializable接口,那么.NET formatter就會(huì)給接口的方法讓步,讓它們負(fù)責(zé)拷貝需要串行化到一個(gè)內(nèi)存緩沖器中的所有的信息。DataTable和DataSet類都通過(guò)ISerializable接口支持串行化。
如果你將一個(gè)DataTable或一個(gè)DataSet串行到一個(gè)二進(jìn)制(binary stream)中,你應(yīng)該可以得到非常緊湊的輸出結(jié)果。雖然你得到的結(jié)果文件是最小的,但遺憾的是,它實(shí)際上并不小;闹嚨氖,你保存到一個(gè)二進(jìn)制的DataSet比你用WriteXml方法保存到XML的同樣的DataSet要大很多。
要解釋這種情況,我們需要來(lái)看看ADO.NET對(duì)象是用什么方式被串行起來(lái)的。在串行一個(gè)DataSet對(duì)象時(shí),它將基于XML的DiffGram表示法保存在formatter的緩沖器中。在串行一個(gè)DataTable時(shí),它首先創(chuàng)建了一個(gè)臨時(shí)的DataSet對(duì)象,將它定義為它的parent,然后作為一個(gè)DiffGram串行起來(lái)。
一個(gè)DiffGram是一個(gè)XML流,它提供了一個(gè)DataSet中表和行的有狀態(tài)的表示法。一個(gè)DiffGram文件是很詳細(xì)的,有些冗長(zhǎng)。DiffGram包含當(dāng)前的數(shù)據(jù),以及被修改的行和未解決的錯(cuò)誤的初始值。當(dāng)我們保存一個(gè)DataSet或一個(gè)DataTable時(shí),所有這些信息就會(huì)被傳遞給serializer。被串行化的對(duì)象總是包含XML數(shù)據(jù),因此即使當(dāng)輸出流是二進(jìn)制的時(shí),最后的輸出結(jié)果仍然會(huì)很大。
你可以創(chuàng)建一個(gè)繼承DataTable或DataSet的新的可串行化的類來(lái)解決這個(gè)問(wèn)題,并且更有效地保存ADO.NET對(duì)象。你必須用<Serizlizable()>屬性來(lái)標(biāo)記新類,即使父類是可以串行化的。實(shí)際上,串行性(serizlizability)并不是一個(gè)可以自動(dòng)繼承的類屬性。你從DataTable或DataSet構(gòu)建的新類也可以實(shí)現(xiàn)ISerializable接口。當(dāng)然,你可以為新類選擇一個(gè)不同的串行化方案。一個(gè)簡(jiǎn)單而有效的方法就是將DataTable類的所有成員映射到數(shù)組和值成員中(見(jiàn)列表1)。
運(yùn)用一個(gè)派生的類和一個(gè)自定義的串行化方案可以為一個(gè)DataSet對(duì)象節(jié)省多達(dá)80%的磁盤空間。節(jié)省的空間的比率取決于DataSet中的數(shù)據(jù)類型。你的數(shù)據(jù)越基于文本,節(jié)省的空間越多。然而,運(yùn)用二進(jìn)制的BLOB字段只可以節(jié)省大約25%的空間(下載一個(gè)完整的例子)。
9. 選擇一個(gè)適合你的數(shù)據(jù)的分頁(yè)機(jī)制 DataGrid服務(wù)器控件使我們可以更容易地在Web應(yīng)用程序中以長(zhǎng)度可變的頁(yè)面來(lái)顯示數(shù)據(jù)了。該控件有綁定和格式化功能,它可以接受一個(gè)ADO.NET數(shù)據(jù)對(duì)象并為瀏覽器生成HTML代碼。出于性能的原因,在頁(yè)面的視圖狀態(tài),DataGrid并沒(méi)有緩存數(shù)據(jù)源的內(nèi)容。因此,當(dāng)返回頁(yè)面時(shí),你就必須填充grid。要實(shí)現(xiàn)這一點(diǎn)可以用兩種方法:在Web服務(wù)器上將數(shù)據(jù)源作為整體或一部分緩存起來(lái),然后讀回;或者對(duì)每個(gè)請(qǐng)求從物理數(shù)據(jù)庫(kù)加載所需的記錄。如果你選擇第一種方法,那么數(shù)據(jù)就從存儲(chǔ)中只被讀取一次,保存在一個(gè)緩存中,并為以后的postback事件讀回。我們通常用內(nèi)存中的全局對(duì)象(如Session或Cache)來(lái)保存這個(gè)數(shù)據(jù)。我們用DataSet來(lái)搜集所有需要的數(shù)據(jù)并將它保存在內(nèi)存中。將一個(gè)DataSet對(duì)象保存在Session中同ADO中的線程含義并不一樣,但是通過(guò)減少Web服務(wù)器可用的內(nèi)存仍可以影響可擴(kuò)展性。
如果要顯示的數(shù)據(jù)是特定于session的,那么在每次返回頁(yè)面時(shí)加載記錄頁(yè)面就比用一個(gè)DataSet和ASP.NET全局對(duì)象來(lái)緩存數(shù)據(jù)要好。編寫得很好的SQL代碼可以將結(jié)果集分成許多頁(yè),再加上DataGrid控件內(nèi)置的自定義分頁(yè)機(jī)制,我們就可以得到最佳的解決方案來(lái)保持ASP.NET應(yīng)用程序的可擴(kuò)展性和良好的性能了。
對(duì)于Windows應(yīng)用程序,我的建議正好相反。臺(tái)式應(yīng)用程序很適合應(yīng)用斷開(kāi)的編程模式(DataSet和其它ADO.NET對(duì)象使這種模式變得更簡(jiǎn)單了)。當(dāng)然,這并不意味著,你可以在客戶端無(wú)憂無(wú)慮地下載成千上萬(wàn)的記錄。盡管你可以將ADO.NET對(duì)象用于任何種類的.NET應(yīng)用程序,但如何使用它們是隨具體情況的不同而不同的。
10. 訪問(wèn)多個(gè)結(jié)果集 根據(jù)查詢的語(yǔ)法,你可以返回多個(gè)結(jié)果集。缺省情況下,data reader是位于第一個(gè)結(jié)果集上的。你可以用Read方法在當(dāng)前結(jié)果集中滾動(dòng)查看記錄。在找到最后一個(gè)記錄時(shí),Read方法返回false,不再繼續(xù)讀取。你應(yīng)該用NextResult方法轉(zhuǎn)移到下一個(gè)結(jié)果集。如果沒(méi)有更多的需要讀的結(jié)果集了,那么該方法返回false。下面的代碼說(shuō)明了如何在所有返回的結(jié)果集中訪問(wèn)所有的記錄:Dim reader As SqlDataReader cmd.Connection.Open() reader = cmd.ExecuteReader() Do ' Move through the first resultset While reader.Read() ' access the row End While Loop While reader.NextResult() reader.Close() cmd.Connection.Close()
當(dāng)你讀一個(gè)行的內(nèi)容時(shí),可以通過(guò)索引或名稱來(lái)識(shí)別列。運(yùn)用索引可以更快,因?yàn)樘峁┱呖梢灾苯舆M(jìn)入到緩沖器中。如果你指定列名,提供者就用GetOrdinal方法將名稱轉(zhuǎn)換成相應(yīng)的索引,然后執(zhí)行基于索引的訪問(wèn)。注意,對(duì)于SQL Server data reader來(lái)說(shuō),所有的GetXXX方法實(shí)際上都調(diào)用了相應(yīng)的GetSqlXXX方法。對(duì)于Oracle data reader來(lái)說(shuō),情況是類似的,本地?cái)?shù)據(jù)總是被寫進(jìn).NET Framework類型中。OracleDataReader類為它自己的內(nèi)部類型提供了一組私有的GetXXX方法。這些方法包括GetOracleBFile、GetOracleBinary和GetOracleDateTime等。相反,OLE DB和ODBC readers只有單獨(dú)的一組get方法。
.NET Framework 1.1版通過(guò)添加方法HasRows擴(kuò)展了data readers的編程接口,該方法返回一個(gè)Boolean值來(lái)說(shuō)明是否有很多行需要讀。(這是ASP.NET 1.0的一個(gè)不足之處。)然而,該方法并沒(méi)有告訴我們有效的行的數(shù)量。同樣,也沒(méi)有方法或技巧使我們提前知道已經(jīng)返回了多少結(jié)果集。
在Oracle數(shù)據(jù)庫(kù)編程中,一個(gè)查詢或一個(gè)存儲(chǔ)過(guò)程返回的多個(gè)結(jié)果集是通過(guò)多個(gè)REF CURSOR對(duì)象處理的。有多少結(jié)果集,你就必須將多少輸出參數(shù)同命令關(guān)聯(lián)起來(lái),以便NextResult方法可以用于Oracle數(shù)據(jù)庫(kù)。在命令文本中,一個(gè)ADO.NET結(jié)果集同一個(gè)Oracle REF CURSOR是一致的。輸出參數(shù)名必須與指針名匹配,它們的類型必須是OracleType.Cursor。例如,如果要運(yùn)行的存儲(chǔ)過(guò)程(或命令文本)引用了兩個(gè)指針(Employees和Orders),那么下面的代碼就說(shuō)明了如何進(jìn)行設(shè)置以返回兩個(gè)結(jié)果集:Dim p1 As OracleParameter p1 = cmd.Parameters.Add("Employees", OracleType.Cursor) p1.Direction = ParameterDirection.Output Dim p2 As OracleParameter p2 = cmd.Parameters.Add("Orders", OracleType.Cursor) p2.Direction = ParameterDirection.Output
在上面的代碼中,cmd是一個(gè)OracleCommand對(duì)象,它指向一個(gè)命令或一個(gè)存儲(chǔ)過(guò)程。它執(zhí)行代碼,創(chuàng)建了兩個(gè)REF CURSOR,稱為Employees和Orders。REF CURSOR的名稱和ADO.NET輸出參數(shù)的名稱必須匹配。
ADO.NET對(duì)象模式包含兩個(gè)主要的部分——托管提供者和database-agnostic的容器類,如DataSet。托管提供者是數(shù)據(jù)源連接器的新類型;它們代替了基于COM的OLE DB提供者。到我寫這篇文章時(shí)為止,只有少數(shù)幾個(gè)托管提供者來(lái)連接商業(yè)DBMS。.NET Framework 1.1版只包含幾個(gè)本地提供者——用于SQL Server、Oracle和所有OLE DB的提供者和ODBC驅(qū)動(dòng)程序。第三方的供應(yīng)商也支持MySQL并為Oracle提供了可供選擇的提供者。
ADO.NET看起來(lái)類似于ADO,而且托管提供者在結(jié)構(gòu)上同OLE DB提供者也是可以相比的。除了這些相似點(diǎn)外,在ADO.NET中進(jìn)行有效的編程還需要一套新的技巧和好的方法。在大多數(shù)情況下,你可以通過(guò)編寫代碼得到很多技巧,并積累對(duì)象模式方面的經(jīng)驗(yàn)。當(dāng)你在進(jìn)一步研究ADO.NET編程時(shí),記住我在本文中所講的這10個(gè)ADO.NET技巧吧。
關(guān)于作者: Dino Esposito是Wintellect(www.wintellect.com)的培訓(xùn)講師和顧問(wèn),他主要負(fù)責(zé)ADO.NET和XML課程。他是Building Web Solutions with ASP.NET and ADO.NET和Applied XML Programming for Microsoft.NET(Microsoft Press)的作者,也是www.vb2themax.com的協(xié)辦人。他的聯(lián)系方式是dinoe@wintellect.com。
|
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!