掌握ADO.NET的十個熱門技巧

來源:互聯網
上載者:User
ado|技巧 ADO.NET提供了一個統一的編程模式和一組公用的類來進行任何類型的資料訪問,而不管你用何種語言來開發代碼。ADO.NET是全新的,但又與ADO儘可能保持一致,它使編程模式從一個用戶端/伺服器、基於串連的模式轉變到了一個新的模式,這個新模式可以讓斷開的前端下載記錄、離線工作、然後重新串連來提交變化。ADO.NET是WinForms應用程式、ASP.NET應用程式和Web services的一個共有的特點。其功能可以跨LAN和Internet串連來實現,可以在有狀態(stateful)和無狀態(stateless)情況下實現。

這就意味著,作為一個共有的技術,ADO.NET的對象在所有可能的環境中並不是同等強大的。用ADO.NET為一個富用戶端(rich client)構建一個資料層同為一個用戶端通常是共用的和重要的實體(如Web伺服器)的Web應用程式構建一個資料層並不一樣。

如果你從前是個ADO開發人員,現在已經用ADO.NET了,那麼你可能把資料訪問看做是一個萬能的對象,如Recordset。我們很自然地會將舊的對象模式同新的對象模式比對起來,並將現有的方法用於.NET應用程式。然而,在ADO環境中的某些好的方法在轉換到ADO.NET環境時就可能並不強大了。而且,看起來很微不足道的ADO.NET對象模式的複雜性可能會導致很糟糕的編程情況、不理想的代碼、甚至是功能不能實現。我將講述在ADO.NET編程中可能會給你帶來麻煩的10個方面,並提供技巧和解決方案來避免它們。

1. 避免Database-Agnostic形式的編程
ADO.NET中的資料訪問是強型別的,就是說在任何時候你都必須瞭解你正在處理的是什麼資料來源(data source)。相反,在ADO中,你可以編寫資料存取碼(它們充分利用了OLE DB提供者的通用模式),並將基本的資料來源只看做是個參數。ADO對象模式提供了唯一的串連和命令對象,它們隱藏了基本的DBMS的特徵。一旦你在Connection對象上設定了Provider屬性,那麼為SQL Server或Oracle建立一個命令對象就需要同樣的代碼。許多開發人員都通過該功能來使用生產環境外的Access資料庫,以便很快地測試或示範應用程式。

在ADO.NET中是不能這麼做的,因為在ADO.NET中,至少連線物件必須是特定於資料來源的。你不能以一種間接或通用的方式來建立串連,除非你決定運用ADO的資料訪問技術——OLE DB。在ADO.NET中,你可以用OleDbConnection類建立到一個資料庫的串連,這個類可以讓你訪問各種資料來源。在.NET託管環境中運用System.Data.OleDb名字空間中的類並不特別有效,因為它們是用OLE DB來訪問資料的。你只能用OLE DB來訪問那些沒有.NET資料提供者的資料來源。

如果你的應用程式必須訪問全異的資料來源(而且你知道可能涉及什麼資料來源——一個合理的假設),那麼你可以建立一個集中的factory類,它返回一個連線物件,並通過一個通用的介面(IDbConnection介面)來管理這個連線物件。Factory類在內部運用應用程式參數來決定使用什麼.NET資料提供者:' Create the connection
Dim factory As New MyAppConnectionFactory
Dim conn As IDbConnection
conn = factory.CreateConnection(connString)

' Create the command
Dim cmd As IDbCommand = conn.CreateCommand(query)




一旦你得到了一個連線物件,你就可以以database-agnostic的方式來建立和執行一個命令了,而不管使用的資料來源是什麼。你可以使用CreateCommand方法並通過IDbCommand介面來引用命令。然後,你可以用IDbCommand介面上的ExecuteReader方法或ExecuteNonQuery方法來執行命令。如果你用ExecuteReader,你就可以得到一個data reader並可以用IDataReader介面來對它進行一般的訪問了。

你不能用一個通用的資料庫編程模式來填充一個DataSet對象。實際上,你不能像建立一個命令那樣以一種間接的方式來建立data adapter對象。原因就是,在有些情況下,data adapter不同於命令對象,它可以在內部隱含地建立一個串連。然而,它必須以一種強型別的方式工作,而且必須知道基本的資料庫伺服器是什麼。

2. 運用字串來序列化擴充的屬性
幾個ADO.NET對象都擁有一個叫做ExtendedProperties的集合。該屬性就像收集貨物(cargo collection)一樣,可以用來儲存任何類型的使用者資訊。DataSet、DataTable和DataColumn就是可以提供該資料成員的類。ADO.NET通過運用PropertyCollection類封裝的一個雜湊表來實現這個ExtendedProperties屬性。你可以用Add方法將資料插入到集合中。Add方法使用了兩個參數來儲存資料——key和value。該方法的原形將參數定義為通用的物件類型,你可以儲存任何類型的資訊。然而,在特殊情況下,你應該特別注意那些被儲存為擴充屬性的對象的類型。

如果你想將包含擴充屬性的ADO.NET對象序列化到XML,最好只用字串。如果不行,你必須對ADO.NET的內在的serializer的行為採取對策。

當ADO.NET將一個DataSet對象儲存到XML時,ExtendedProperties集合的內容就被序列化到記憶體中了,但大概是出於效能的原因,ADO.NET運用了ToString方法,而不是XML serializer來實現序列化。更重要的是,當ADO.NET對象被讀回並複原時,ExtendedProperties集合包含的是對象的字串表現形式,而不是對象本身。

3. 運用具有BLOB欄位的ExecuteXmlReader
用於SQL Server的.NET資料提供者(data provider)使用了資料庫提供的XML副檔名,並提供了一個額外的方法(ExecuteXmlReader)來執行查詢。命令對象上的所有的執行者(例如ExecuteReader和ExecuteScaler)都採用不同的方法來得到結果集。ExcecuteReader通過一個託管指標(managed cursor)(data reader)來返回資料,而ExecuteScaler返回結果集中的第一個值,把它作為一個標量值。ExecuteXmlReader執行查詢,並返回已經綁定到一個XmlTextReader對象的基於XML的輸出資料流。通過這種方式,你就不需要做額外的工作來以XML的方式加工資料了。要實現這一點,查詢字串必須返回XML資料。對SQL Server來說,當查詢字串包含一個FOR XML子句時,就可以實現它。儘管這隻是一種可能。

一個不太為人所知的情況是,要使ExecuteXmlReader工作,讓結果集包含XML資料就足夠了。 下面的查詢方法很好,只要列包含XML格式的文本就行:SELECT data FROM table WHERE key=1

這個列是個典型的BLOB或ntext欄位,其文本顯示為XML。簡要地看看ExecuteXmlReader方法的內部結構會有助於我們的理解。該方法用ExecuteReader來執行查詢,並從資料提供者得到一個資料流對象。接下來,它將資料流綁定到XmlTextReader類的一個新建立的執行個體上,這個執行個體被返回給調用者。串連一直處於忙碌狀態,直到XML reader停止工作。SQL Server提供者是唯一的提供者,它提供了方法讓我們從一個XML reader直接讀取資料,但這種做法更多的是與提供者有關,而與資料庫效能的關係並不大。Oracle支援XML查詢,但Oracle的資料提供者並不支援XML查詢。相比之下,為OLE DB資料提供者編寫一個ExecuteXmlReader方法並不難(點此下載執行個體)。

4. 不要設法緩衝一個DataView
DataSet和DataTable對象是唯一的包含資料的ADO.NET對象。DataView是一個不能序列化的、輕量級的類,它只代表構建在一個表上的視圖(view)。你可以根據一個運算式或行的狀態來過濾視圖。許多應用程式都需要你管理資料檢視並將它們綁定到資料控制項上,如Windows和Web DataGrid控制項。一個DataView對象不能快取資料;它只是緩衝了與當前過濾器相匹配的基本的表中的行的索引。緩衝索引的順序與當前的排序運算式一致。緩衝DataView而不緩衝基本的DataTable是不行的。

例如,提供分頁(比如通過運用DataGrid控制項)的ASP.NET應用程式通常以一個DataView對象結尾,因為它支援排序和過濾。在有些情況下(大多是基於效能的原因),你可能決定要快取資料源。要緩衝的對象不能是DataView(它是你實際綁定的對象)。一個DataView只是一種索引,如果沒有基本的DataTable對象,它是沒有用的。

5. 運用Find來讀取一個記錄
通過運用DataTable的Select方法來運行一個記憶體中的查詢,或在視圖上設定一個過濾器來濾掉與指定標準不匹配的所有的記錄,你就可以讀取一個DataTable對象中的一個特定的行了。你可以通過設定DataView類上的RowFilter屬性來設定一個過濾器。這兩種方法都運用相同的引擎來選擇記錄。它們可以接納一個運算式,對它進行解析並求各個子句的值。DataTable的Select方法返回一個帶有所有相匹配的DataRow對象的數組。RowFilter屬性重建DataView的內部索引來包含所有的(且僅包含)匹配的記錄。然後,應用程式就可以訪問記錄了。這兩種方法在效能上幾乎是一樣的;運用哪種方法取決於環境和個人喜好。例如,如果你用的是資料繫結的控制項,如一個DataGrid或DataList,那麼RowFilter就很理想。如果你必須處理一串記錄,那麼Select方法就更好了。

然而,你還可以用另一種方法(仍然是基於DataView的),它是讀取一個表中的記錄的最快的方法。該方法就是用Find:Dim view As DataView
view = New DataView(table)
view.Sort = "orderid"
Dim index As Integer = view.Find(10248)
Dim row As DataRow = view(index).Row




Find方法運用了視圖的當前索引,並將指定的值(或多個值)與形成當前索引的欄位匹配起來。在前面的代碼中,值10248與列orderid匹配。如果Sort屬性為空白,且DataTable對象有一個主鍵,那麼就運用主鍵中的列。Find方法返回的是相匹配的第一行的基於0的位置的值。

如果你想返回多個記錄,可以用FindRows的演變形式: view.Sort = "orderid, discount"
Dim keys(1) As Object
keys(0) = 10248
keys(1) = 0
Dim row As DataRow = _
view(view.Find(keys)).Row




前面的代碼可以讓你通過運用Find的重載方法(帶有一組對象)來匹配多個列的值。

6. 儘可能用預先排序的資料
ADO.NET對象模式使我們可以很容易地實現排序。你可以建立一個DataView對象並設定其Sort屬性;ADO.NET runtime查看新的排序運算式並為視圖重編索引。該步驟是在記憶體中實現的,但速度並不快。排序的花費很高,更重要的是,它並不是個線性操作(linear operation)。對一組資料進行排序需要n*log(n)的計算成本,就是說,隨著需要排序的條目數量的增加,直線增加的成本是很大的。因此,你應該限制應用程式中的排序,儘可能地運用預先排序的資料。在Web應用程式中,動態排序對效能的影響是相當大的。既然如此,你就應該設計應用程式,限制對動態排序的需求,並依賴在資料庫伺服器中寫死的演算法。除非你在用應用程式的一個可以使複雜性低於n*log(n)極限的特殊的功能,否則避免運用手工排序演算法,因為這種演算法可能比系統中的演算法更糟。

7. ADOX可以幫你得到並改變Schema資訊
ADO.NET並沒有為得到並管理schema資訊提供一個完全的對象模式。你應該用ActiveX Data Objects Extensions for Data Definition Language and Security (ADOX)或用每個資料庫提供的本地功能來得到並改變Schema資訊。ADOX是ADO對象的一個擴充,它包括用來建立和修改Schema的對象。你可以編寫適用於各種資料來源的代碼(不管本地文法有什麼不同),因為ADOX是管理schema的一個基於對象的方法。

你可以用一個data reader對象來讀(不是設定)簡單的schema資訊。所有的data reader類(OleDbDataReader、SqlDataReader、OracleDataReader)都提供了一個GetSchemaTable方法,該方法可以讀取查詢到的列的中繼資料資訊。GetSchemaTable返回一個DataTable對象(格式是每列一行)和固定的一組包含資訊的列。返回的中繼資料可以分成三類:列中繼資料、資料庫特徵和列屬性。返回的列可以是AllowDBNull、IsAutoIncrement、ColumnName、IsExpression、IsReadonly和NumericPrecision等。在MSDN資料中有完整的列表(見附加資源)。

在調用ExecuteReader時,如果你執行KeyInfo命令,那麼GetSchemaTable方法就可以返回更精確的資料。你可以將KeyInfo行為同預設的行為結合起來,執行一個單獨的命令並得到schema和資料:reader = cmd.ExecuteReader( _
CommandBehavior.KeyInfo Or _
CommandBehavior.CloseConnection)



只有執行KeyInfo,IsKey、BaseTableName、IsAliased、IsExpression和IsHidden欄位的值才能被正確返回。如果執行KeyInfo,關鍵的列(如果有)通常是添加在結果集的底部的,但不給它們返回資料。

8. 用一個派生的類和自訂的序列化來節省空間的
只有兩個ADO.NET對象是被標記為可序列化的——DataTable和DataSet。.NET Framework中的序列化是通過formatter對象來完成的,它們可以將一個對象執行個體儲存到一個二進位或一個SOAP流(stream)中。.NET formatter用Reflection來提取任何必要的資訊。然而,如果這個類實現了ISerializable介面,那麼.NET formatter就會給介面的方法讓步,讓它們負責拷貝需要序列化到一個記憶體緩衝器中的所有的資訊。DataTable和DataSet類都通過ISerializable介面支援序列化。

如果你將一個DataTable或一個DataSet串列到一個二進位(binary stream)中,你應該可以得到非常緊湊的輸出結果。雖然你得到的結果檔案是最小的,但遺憾的是,它實際上並不小。荒謬的是,你儲存到一個二進位的DataSet比你用WriteXml方法儲存到XML的同樣的DataSet要大很多。

要解釋這種情況,我們需要來看看ADO.NET對象是用什麼方式被串列起來的。在串列一個DataSet對象時,它將基於XML的DiffGram標記法儲存在formatter的緩衝器中。在串列一個DataTable時,它首先建立了一個臨時的DataSet對象,將它定義為它的parent,然後作為一個DiffGram串列起來。

一個DiffGram是一個XML流,它提供了一個DataSet中表和行的有狀態的標記法。一個DiffGram檔案是很詳細的,有些冗長。DiffGram包含當前的資料,以及被修改的行和未解決的錯誤的初始值。當我們儲存一個DataSet或一個DataTable時,所有這些資訊就會被傳遞給serializer。被序列化的對象總是包含XML資料,因此即使當輸出資料流是二進位的時,最後的輸出結果仍然會很大。

你可以建立一個繼承DataTable或DataSet的新的可序列化的類來解決這個問題,並且更有效地儲存ADO.NET對象。你必須用<Serizlizable()>屬性來標記新類,即使父類是可以序列化的。實際上,串列性(serizlizability)並不是一個可以自動繼承的類屬性。你從DataTable或DataSet構建的新類也可以實現ISerializable介面。當然,你可以為新類選擇一個不同的序列化方案。一個簡單而有效方法就是將DataTable類的所有成員映射到數組和值成員中。

運用一個派生的類和一個自訂的序列化方案可以為一個DataSet對象節省多達80%的磁碟空間。節省的空間的比率取決於DataSet中的資料類型。你的資料越基於文本,節省的空間越多。然而,運用二進位的BLOB欄位只可以節省大約25%的空間(下載一個完整的例子)。

9. 選擇一個適合你的資料的分頁機制
DataGrid伺服器控制項使我們可以更容易地在Web應用程式中以長度可變的頁面來顯示資料了。該控制項有綁定和格式化功能,它可以接受一個ADO.NET資料對象並為瀏覽器產生HTML代碼。出於效能的原因,在頁面的檢視狀態,DataGrid並沒有快取資料源的內容。因此,當返回頁面時,你就必須填充grid。要實現這一點可以用兩種方法:在Web伺服器上將資料來源作為整體或一部分緩衝起來,然後讀回;或者對每個請求從物理資料庫載入所需的記錄。如果你選擇第一種方法,那麼資料就從儲存中只被讀取一次,儲存在一個緩衝中,並為以後的postback事件讀回。我們通常用記憶體中的全域對象(如Session或Cache)來儲存這個資料。我們用DataSet來搜集所有需要的資料並將它儲存在記憶體中。將一個DataSet對象儲存在Session中同ADO中的線程含義並不一樣,但是通過減少Web伺服器可用的記憶體仍可以影響可擴充性。

如果要顯示的資料是特定於session的,那麼在每次返回頁面時載入記錄頁面就比用一個DataSet和ASP.NET全域對象來快取資料要好。編寫得很好的SQL代碼可以將結果集分成許多頁,再加上DataGrid控制項內建的自訂分頁機制,我們就可以得到最佳的解決方案來保持ASP.NET應用程式的可擴充性和良好的效能了。

對於Windows應用程式,我的建議正好相反。台式應用程式很適合應用斷開的編程模式(DataSet和其它ADO.NET對象使這種模式變得更簡單了)。當然,這並不意味著,你可以在用戶端無憂無慮地下載成千上萬的記錄。儘管你可以將ADO.NET對象用於任何種類的.NET應用程式,但如何使用它們是隨具體情況的不同而不同的。

10. 訪問多個結果集
根據查詢的文法,你可以返回多個結果集。預設情況下,data reader是位於第一個結果集上的。你可以用Read方法在當前結果集中滾動查看記錄。在找到最後一個記錄時,Read方法返回false,不再繼續讀取。你應該用NextResult方法轉移到下一個結果集。如果沒有更多的需要讀的結果集了,那麼該方法返回false。下面的代碼說明了如何在所有返回的結果集中訪問所有的記錄: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()




當你讀一個行的內容時,可以通過索引或名稱來識別列。運用索引可以更快,因為提供者可以直接進入到緩衝器中。如果你指定列名,提供者就用GetOrdinal方法將名稱轉換成相應的索引,然後執行基於索引的訪問。注意,對於SQL Server data reader來說,所有的GetXXX方法實際上都調用了相應的GetSqlXXX方法。對於Oracle data reader來說,情況是類似的,本機資料總是被寫進.NET Framework類型中。OracleDataReader類為它自己的內部類型提供了一組私人的GetXXX方法。這些方法包括GetOracleBFile、GetOracleBinary和GetOracleDateTime等。相反,OLE DB和ODBC readers只有單獨的一組get方法。

.NET Framework 1.1版通過添加方法HasRows擴充了data readers的編程介面,該方法返回一個Boolean值來說明是否有很多行需要讀。(這是ASP.NET 1.0的一個不足之處。)然而,該方法並沒有告訴我們有效行的數量。同樣,也沒有方法或技巧使我們提前知道已經返回了多少結果集。

在Oracle資料庫編程中,一個查詢或一個預存程序返回的多個結果集是通過多個REF CURSOR對象處理的。有多少結果集,你就必須將多少輸出參數同命令關聯起來,以便NextResult方法可以用於Oracle資料庫。在命令文本中,一個ADO.NET結果集同一個Oracle REF CURSOR是一致的。輸出參數名必須與指標名匹配,它們的類型必須是OracleType.Cursor。例如,如果要啟動並執行預存程序(或命令文本)引用了兩個指標(Employees和Orders),那麼下面的代碼就說明了如何進行設定以返回兩個結果集: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是一個OracleCommand對象,它指向一個命令或一個預存程序。它執行代碼,建立了兩個REF CURSOR,稱為Employees和Orders。REF CURSOR的名稱和ADO.NET輸出參數的名稱必須匹配。

ADO.NET對象模式包含兩個主要的部分——託管提供者和database-agnostic的容器類,如DataSet。託管提供者是資料來源連接器的新類型;它們代替了基於COM的OLE DB提供者。到我寫這篇文章時為止,只有少數幾個託管提供者來串連商業DBMS。.NET Framework 1.1版只包含幾個本地提供者——用於SQL Server、Oracle和所有OLE DB的提供者和ODBC驅動程式。第三方的供應商也支援MySQL並為Oracle提供了可供選擇的提供者。

ADO.NET看起來類似於ADO,而且託管提供者在結構上同OLE DB提供者也是可以相比的。除了這些相似點外,在ADO.NET中進行有效編程還需要一套新的技巧和好的方法。在大多數情況下,你可以通過編寫代碼得到很多技巧,並積累對象模式方面的經驗。當你在進一步研究ADO.NET編程時,記住我在本文中所講的這10個ADO.NET技巧吧。



相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。