直播中
.NET 中的數(shù)據(jù)訪問
讀取數(shù)據(jù)
DataSet、DataTable 和 Recordset
轉(zhuǎn)換現(xiàn)有代碼
更新數(shù)據(jù)
XML 擴(kuò)展支持
總結(jié)
自若干年前推出開放式數(shù)據(jù)庫連接 (ODBC) 應(yīng)用程序編程接口 (API) 以來,出現(xiàn)了各種各樣的數(shù)據(jù)庫訪問技術(shù),而 ADO.NET 是其中最新的一種。在這過程中,發(fā)生了許多有趣的事。例如,COM 闖入數(shù)據(jù)庫領(lǐng)域,開始培植 OLE DB 的殖民進(jìn)程。然后,大致相當(dāng)于 OLE DB 自動化版本的 ActiveX® Data Objects (ADO) 被選來統(tǒng)治 Windows® 數(shù)據(jù)庫開發(fā)者的 Visual Basic® 和 ASP 社區(qū)。
通過 .NET,Microsoft 正在提供通用框架(即 Framework Class Library),其中將包括所有現(xiàn)有的 Windows API 甚至更多的內(nèi)容。特別值得一提的是,它包括大量常用的庫,而這些庫現(xiàn)在需要通過各個 COM 對象分別獲得。在這些庫中,您會發(fā)現(xiàn) XML 和 ADO 對象模型,它們被集成到了叫做 ADO.NET 的類子樹中。
ADO.NET 事實上成為構(gòu)建數(shù)據(jù)感知 .NET 應(yīng)用程序的基礎(chǔ)。和 ADO 不同的是,ADO.NET 遵循更通用的原則,不那么專門面向數(shù)據(jù)庫。ADO.NET 集合了所有允許數(shù)據(jù)處理的類。這些類表示具有典型數(shù)據(jù)庫功能(如索引、排序和視圖)的數(shù)據(jù)容器對象。盡管 ADO.NET 是 .NET 數(shù)據(jù)庫應(yīng)用程序的權(quán)威解決方案,但從總體設(shè)計上來看,它不象 ADO 模型那樣以數(shù)據(jù)庫為中心,這是 ADO.NET 的一大特點(diǎn)。
ADO.NET 與 ADO 有很大差異。ADO.NET 是新的數(shù)據(jù)訪問編程模型,需要開發(fā)人員的全面理解、投入和新思維。然而,一旦開始掌握 ADO.NET,您將意識到:原有的 ADO 技巧非常有助于您以不同、卻更巧妙和可靠的方式來創(chuàng)建有效的應(yīng)用程序和解決各種老問題。
在這篇文章的其余部分,我將集中介紹如何以 ADO.NET 方式實現(xiàn)基本的數(shù)據(jù)庫操作。我想說明,在什么時候 ADO.NET 是比 ADO 更好的選擇,而您最好在什么時候應(yīng)放棄 ADO。ADO.NET 并不是將 ADO 改良以符合 .NET 基礎(chǔ)結(jié)構(gòu)而形成的。只要您看一下 ADO.NET 的語法、代碼設(shè)計和移植,就會明白這一點(diǎn)。
.NET 中的數(shù)據(jù)訪問
在 ADO.NET 中訪問數(shù)據(jù)源的方式由托管提供程序確定。從功能上講,托管提供程序與 OLE DB 的提供程序非常相似,但有兩個重要的不同之處。首先,管理提供程序在 .NET 環(huán)境中工作,通過 DataReader 和 DataTable 等 .NET 類檢索和公開數(shù)據(jù)。其次,因為它們的體系結(jié)構(gòu)針對 .NET 進(jìn)行了優(yōu)化,所以比較簡單。
目前 ADO.NET 提供了兩種托管提供程序:一種用于 SQL Server™ 7.0 或更高版本,另一種用于其他所有您可能已經(jīng)安裝的 OLE DB 提供程序。在這兩種情況下您分別使用不同的類,但遵循相似的命名規(guī)則。除前綴外,名稱都是相同的。前一種情況前綴為 SQL,后一種情況則是 ADO。
您應(yīng)該使用 SQL 類訪問 SQL Server 表,因為它們直接進(jìn)入數(shù)據(jù)庫服務(wù)器的內(nèi)部 API,跳過了由 OLE DB 提供程序表示的中間層。ADO 類是 OLE DB 提供程序上的 .NET 接口,它們使用 COM Interop 橋進(jìn)行工作。
ADO.NET 對象的初學(xué)者可參閱 Omri Gazitt 的文章介紹 ADO+:用于 Microsoft .NET 框架的數(shù)據(jù)訪問服務(wù)(英文)和我的 ADO+ 推動數(shù)據(jù)種類的演變(英文)一文。前者技術(shù)性較強(qiáng),針對 ADO.NET 程序模型提供了高水平的評注性概述。后者主要介紹 ADO.NET 的目標(biāo)和它與 XML、腳本以及其他技術(shù)之間的聯(lián)系。
讀取數(shù)據(jù)
需要從數(shù)據(jù)源中讀取數(shù)據(jù)的 ADO.NET 應(yīng)用程序首先要創(chuàng)建連接對象。根據(jù)目標(biāo)提供程序的不同,該連接對象可以是 SQLConnection 或 ADOConnection。請記住,您可以使用 ADO.NET 類來連接到 SQL Server 數(shù)據(jù)庫,但我們不建議這樣做。其唯一的缺點(diǎn)是,您的代碼要通過不必要的額外代碼層。它先將 ADO 的托管提供程序調(diào)入,然后托管提供程序再調(diào)用 SQL Server OLE DB 提供程序。而 SQL Server 托管提供程序和 OLE DB 提供程序一樣直接操作數(shù)據(jù)。
ADO 和 ADO.NET 連接對象之間的顯著差異是:ADO.NET 連接不支持 CursorLocation 屬性。請注意,這并不是一個文檔錯誤,而是一個有爭議的設(shè)計問題。為了突出以數(shù)據(jù)為中心的原則,ADO.NET 沒有游標(biāo)的顯式實現(xiàn)。
在 ADO 中,您習(xí)慣了用游標(biāo)從數(shù)據(jù)庫或其他任何 OLE DB 兼容的數(shù)據(jù)源中抽取記錄。您可以選擇客戶端或服務(wù)器游標(biāo),每種游標(biāo)都有幾個預(yù)先設(shè)定的游標(biāo)類型。ADO.NET 則設(shè)計為從數(shù)據(jù)源中抽取數(shù)據(jù),并提供新的編程接口來讀取和分析數(shù)據(jù)。
在 ADO 中,您通過指定連接和命令文本來創(chuàng)建 Recordset 對象。對于游標(biāo)的位置和類型,Recordset 有一定策略。您可以按下列方式之一讀取數(shù)據(jù):
在內(nèi)存中創(chuàng)建選定記錄的靜態(tài)副本,然后在從數(shù)據(jù)源斷開連接時根據(jù)需要處理這些記錄。ADO 稱之為靜態(tài)游標(biāo)。
通過快速、僅向前的只讀游標(biāo)來滾動數(shù)據(jù),這種游標(biāo)工作在記錄的靜態(tài)快照中。ADO 稱之為只讀游標(biāo)。
通過服務(wù)器端的兩種游標(biāo)來訪問數(shù)據(jù),這些游標(biāo)需要保持良好的連接,但您可以在各個不同層次上隨時檢測其他已連接的用戶的更改。ADO 稱它們?yōu)殒I集和動態(tài)游標(biāo)。
前兩種方式都在斷開連接的記錄集內(nèi)工作,并從客戶端緩存讀取信息,這是它們的相似之處。另外,在面向 Web 的環(huán)境中和對于新的 n 層系統(tǒng),這兩種方式被證明是使用頻率最高的。
在 ADO 中,以上所有這些方式與不同類型的游標(biāo)相對應(yīng)。您將在本文后面發(fā)現(xiàn),雖然 ADO.NET 有很大不同,但它能實現(xiàn)您用 ADO 可實現(xiàn)的任何功能。只不過您的代碼將從實際數(shù)據(jù)源及其物理存儲媒介和格式中抽取數(shù)據(jù)。
ADO.NET 提供兩個對象來處理從數(shù)據(jù)源中抽取的數(shù)據(jù)。它們是 DataSet 和 DataReader 對象。前者是記錄在內(nèi)存中的緩存,您可以從任何方向隨意訪問和修改。后者是高度優(yōu)化的對象,專為以僅向前方式滾動只讀記錄而設(shè)計。請注意 DataSet 看起來象靜態(tài)游標(biāo),但實際上,在 .NET 中與 ADO 只讀游標(biāo)相對應(yīng)的是 DataReader 對象。
在 ADO.NET 中,不支持服務(wù)器端游標(biāo)。然而,這不意味著您不能使用游標(biāo)。您需要做的是在 .NET 中導(dǎo)入 ADO 類型庫。在項目窗口的 References 節(jié)點(diǎn)上單擊右鍵就行了。導(dǎo)入之后,您便可以開始在應(yīng)用程序中使用本地 ADO 對象了。
盡管我承認(rèn)下決心轉(zhuǎn)向 .NET 是一件很難的事情,但我個人還是建議您考慮用 .NET 重寫現(xiàn)有應(yīng)用程序??梢园淹耆珜?dǎo)入 ADO 作為邁向 .NET 的第一步,這無須投入太多的時間和資源。然而,請記住這只是漫漫長路上的第一步。這絕不是您邁向 .NET 的唯一一步。.NET 具有超值價值的的真正原因在于統(tǒng)一和一致的編程接口以及對本地類的廣泛使用。您可以導(dǎo)入 COM 類型庫,但導(dǎo)入 COM 類型庫只能作為臨時解決方案或者中間步驟,我們并不鼓勵這樣做。
使用 ADO.NET 時,應(yīng)當(dāng)充分考慮到它統(tǒng)一了數(shù)據(jù)容器類編程接口這一事實。無論您打算編寫何種應(yīng)用程序,Windows 窗體、Web 窗體還是 Web 服務(wù),都可以通過同一組類來處理數(shù)據(jù)。不管在后端的數(shù)據(jù)源是 SQL Server 數(shù)據(jù)庫、OLE DB、XML 文件還是一個數(shù)組,您都可以通過相同的方法和屬性來滾動和處理它們的內(nèi)容。
圖 1:Solution Explorer 菜單
如果您堅持在 .NET 中使用 ADO,請準(zhǔn)備面對一些副作用。例如,您需要額外的代碼才能夠從數(shù)據(jù)綁定控件中使用記錄集。
DataSet、DataTable 和 Recordset
在 ADO.NET 中,沒有與 Recordset 對象直接對應(yīng)的對象。最接近的是 DataTable 對象。盡管這兩個對象的功能幾乎一樣,但它們在各自的框架中起不同的作用。
Recordset 是一個大型對象,具有許多 ADO 功能,但還是有所欠缺。Recordset 在很多方面性能優(yōu)良,例如可創(chuàng)建性、斷開連接時仍能工作、功能豐富等等。但是,在某些方面仍然有待提高。例如,由于 Recordset 固有的 COM 特性,通過網(wǎng)絡(luò)進(jìn)行序列化的工作將非常繁重。又如它是二進(jìn)制對象,所以在不同的平臺上運(yùn)行的模塊很難共享它,而且它不能穿過防火墻。另外,Recordset 表示多個記錄的單個表。如果該表是由一個或多個 JOIN 產(chǎn)生的,更新原始數(shù)據(jù)源可能會很困難。如果您要使斷開連接的記錄集和原始數(shù)據(jù)源保持協(xié)調(diào),數(shù)據(jù)源必須能夠識別 SQL。然而,您的記錄集很可能是通過非 SQL 提供程序創(chuàng)建的。
在 ADO.NET 中,ADO Recordset 的所有功能被拆分成幾個較簡單的對象,DataReader 就是其中之一。DataReader 模擬快速、僅向前的只讀游標(biāo)的操作。
DataTable 是一個表示數(shù)據(jù)源的簡單對象。您可以手動構(gòu)造 DataTable,也可以通過 DataSet 命令自動填充它。DataTable 不區(qū)分它所包含的數(shù)據(jù)的來源。該對象允許您在內(nèi)存中處理數(shù)據(jù),以及執(zhí)行瀏覽、排序、編輯、應(yīng)用篩選器、創(chuàng)建視圖等操作。
ADO 中沒有與 DataSet 相對應(yīng)的對象。DataSet 對象是一個容器類,是實現(xiàn) ADO.NET 數(shù)據(jù)抽取的關(guān)鍵對象。DataSet 將一個或多個 DataTable 對象分組。DataTable 通過象行和列這樣的通用集合公開它的內(nèi)容。當(dāng)您嘗試從數(shù)據(jù)表中讀取數(shù)據(jù)時,您可能會經(jīng)過兩個不同的對象層:DataTableMapping 和 DataView。
DataTableMapping 對象描述了數(shù)據(jù)源中的數(shù)據(jù)列和 DataTable 對象之間的映射關(guān)系。當(dāng)填充 DataSet 時,DataSetCommand 對象要使用這個類。它維護(hù)數(shù)據(jù)集中的抽象列和數(shù)據(jù)源中的物理列之間的鏈接。
表的視圖通過 DataView 對象實現(xiàn)。它表示 DataTable 的自定義視圖,可以綁定到特定控件(如 Windows 窗體和 Web 窗體中的數(shù)據(jù)網(wǎng)格)中。該對象相當(dāng)于 SQL CREATE VIEW 語句在內(nèi)存中的實現(xiàn)。
DataSet 中的所有表都可以通過一個公用域放入關(guān)系中。這個關(guān)系由 DataRelation 對象管理。這看起來很象 ADO 的數(shù)據(jù)形成,但有一點(diǎn)重要區(qū)別。您不需要使用數(shù)據(jù)形成語言,您最終會擁有一個非常靈活的結(jié)構(gòu)體系。ADO.NET 導(dǎo)航模型使您可以輕而易舉地從某一張表內(nèi)的主行移入它的所有子行。
DataRelation 對象相當(dāng)于 JOIN 語句在內(nèi)存中的實現(xiàn),可用于建立數(shù)據(jù)類型相同的列的父/子關(guān)系。一旦建立了關(guān)系,就不允許出現(xiàn)任何會破壞這種關(guān)系的更改,如果出現(xiàn)就會導(dǎo)致運(yùn)行時異常。視圖和關(guān)系是實現(xiàn)主表/明細(xì)表架構(gòu)的兩種方式。要記住,視圖只是放在記錄上的掩碼,而關(guān)系是設(shè)置在兩個表的一個或多個列之間的動態(tài)鏈接。如果使用關(guān)系,您不能更改順序或設(shè)置條件。
如果您的代碼需要一對一外鍵關(guān)系,并且不更改數(shù)據(jù),那么您最好不要使用無格式的 JOIN 命令。如果您需要額外的篩選功能,就應(yīng)該使用 ADO.NET 自定義視圖。