上圖顯示了ADO.NET對(duì)象模型中的類(lèi)。左邊的是連接對(duì)象,這些對(duì)象直接與數(shù)據(jù)庫(kù)通信,以管理連接和事務(wù),以及從數(shù)據(jù)庫(kù)檢索數(shù)據(jù)和向數(shù)據(jù)庫(kù)提交所作的更改。右邊的是非連接對(duì)象,允許用戶脫機(jī)處理數(shù)據(jù)。下面我們逐個(gè)了解這些類(lèi)的作用 一、連接對(duì)象 1、.NET數(shù)據(jù)提供程序 .NET數(shù)據(jù)提供程序是一個(gè)類(lèi)的集合,專(zhuān)門(mén)設(shè)計(jì)用來(lái)同特定類(lèi)型的數(shù)據(jù)存儲(chǔ)區(qū)進(jìn)行通信。在.NET Framework中,包含了四種此類(lèi)提供程序:SQL Client.NET數(shù)據(jù)提供程序、Oracle Client.NET數(shù)據(jù)提供程序、ODBC.NET數(shù)據(jù)提供程序和OLE DB.NET數(shù)據(jù)提供程序。 每種.NET數(shù)據(jù)提供程序都實(shí)現(xiàn)相同的基類(lèi):ProviderFactory,Connection,ConnectionStringBuilder,Command,DataReader,Parameter和Transaction,只是其實(shí)際名稱(chēng)取決于該數(shù)據(jù)提供程序。例如SQL Client.NET數(shù)據(jù)提供程序具有SqlConnection類(lèi),而ODBC.NET數(shù)據(jù)提供程序包含OdbcConnection類(lèi)。無(wú)論使用哪種.NET數(shù)據(jù)提供程序,此數(shù)據(jù)提供程序的Connection類(lèi)都通過(guò)相同的基類(lèi)接口實(shí)現(xiàn)相同的基本特性。要對(duì)數(shù)據(jù)存儲(chǔ)區(qū)打開(kāi)一個(gè)連接,可創(chuàng)建此提供程序連接類(lèi)的一個(gè)實(shí)例,設(shè)置此對(duì)象的ConnectionString屬性,然后調(diào)用其Open方法即可。
每個(gè).NET數(shù)據(jù)提供程序都有自己的命名空間。.NET Framework中所包含的4個(gè)提供程序是System.Data命名空間的一個(gè)子集,非連接對(duì)象就位于System.Data命名空間之中。SQL Client數(shù)據(jù)提供程序位于System.Data.SqlClient命名空間中,ODBC.NET 數(shù)據(jù)提供程序位于System.Data.Odbc命名空間中;OLE DB.NET數(shù)據(jù)提供程序則位于System.Data.OleDb命名空間中;Oracle Client.NET數(shù)據(jù)提供程序位于System.Data.OracleClient命名空間中。
為什么微軟要用分離的類(lèi)和庫(kù)? 過(guò)去微軟使用的ADO技術(shù),并沒(méi)有針對(duì)不同的數(shù)據(jù)提供程序,提供不同的類(lèi),為什么在ADO.NET中要這么做,主要出于性能、擴(kuò)展性的考慮:
在使用ADO技術(shù)時(shí),實(shí)際上是將ADO接口用作于數(shù)據(jù)存儲(chǔ)區(qū)進(jìn)行通信時(shí)的“中介”。開(kāi)發(fā)人員告訴ADO,自己希望使用哪個(gè)提供程序,而ADO將開(kāi)發(fā)人員的調(diào)用傳遞給適當(dāng)?shù)臄?shù)據(jù)提供程序,而后該數(shù)據(jù)提供程序就會(huì)執(zhí)行所請(qǐng)求的操作,并通過(guò)ADO庫(kù)返回結(jié)果。 而在ADO.NET中,數(shù)據(jù)提供程序不涉及中間層。開(kāi)發(fā)人員直接與對(duì)應(yīng)的數(shù)據(jù)提供程序通信,該數(shù)據(jù)提供程序使用數(shù)據(jù)存儲(chǔ)區(qū)的低級(jí)編程接口與數(shù)據(jù)存儲(chǔ)區(qū)通信。使用ADO.NET的SQL Client.NET數(shù)據(jù)提供程序域SQL Server通信要快于ADO和SQL Server OLE DB提供程序,原因就在于少涉及一層。
在SQL Server 2000引入XML特性時(shí),ADO開(kāi)發(fā)小組就面臨著一項(xiàng)很有意義的挑戰(zhàn)。為了給ADO添加能使開(kāi)發(fā)人員從SQL Server 2000中檢索XML數(shù)據(jù)的特性,ADO開(kāi)發(fā)小組就必須為OLE DB API以及SQL Server OLE DB提供程序添加新的接口。 .NET數(shù)據(jù)提供程序更容易擴(kuò)展。它們只需要支持相同的基礎(chǔ)接口,并可以在適當(dāng)?shù)臅r(shí)候提供額外的提供程序?qū)iT(mén)功能。OLE DB.NET數(shù)據(jù)提供程序的Command對(duì)象所公開(kāi)的全部方法和屬性,SQL Clien.NET數(shù)據(jù)提供程序的Command對(duì)象(SqlCommand)也均予以公開(kāi),此外還添加了一個(gè)新的方法,以獲得以XML方式返回的程序結(jié)果。 SQL Server 2005包含了一組新的特性,例如能夠使應(yīng)用程序利用通知服務(wù),以便在服務(wù)器上查詢結(jié)果發(fā)生變化時(shí)收到通知。微軟并沒(méi)有改變ADO.NET公開(kāi)類(lèi)的內(nèi)部工作,而只是想SQL Client.NET數(shù)據(jù)提供程序添加了兩個(gè)新類(lèi),以充分利用這些新的SQL Server特性。
2、連接對(duì)象
ProviderFactory類(lèi)是ADO.NET2.0中新增的類(lèi),相當(dāng)于一個(gè)對(duì)象工廠,使開(kāi)發(fā)人員能夠?yàn)?NET數(shù)據(jù)提供程序創(chuàng)建其他類(lèi)的實(shí)例。每個(gè)ProviderFactory都提供一種Create方法,此方法創(chuàng)建Connections,ConnectionStringBuilders,Commands,Parameters,DataAdapters和CommandBuilders。
表示與數(shù)據(jù)庫(kù)的連接??赏ㄟ^(guò)Connection的不同屬性指定數(shù)據(jù)源的類(lèi)型、位置。它起到渠道的作用,其他對(duì)象如DataAdapter和Command通過(guò)它與數(shù)據(jù)庫(kù)進(jìn)行通信,以提交查詢和獲取查詢結(jié)果,下面這幅圖很形象的說(shuō)明了Conneciton所承擔(dān)的橋梁作用。 (備注:這幅圖使用了冠華仔博文http://www.cnblogs.com/gishuazi/archive/2009/03/05/1403823.html中的圖:_))
ConnectionStringBuilder類(lèi)也是ADO.NET2.0中新增的,它簡(jiǎn)化了為.NET數(shù)據(jù)提供程序建立連接字符串的過(guò)程,每個(gè)ConnectionStringBuilder類(lèi)都公開(kāi)一些屬性,這些屬性對(duì)應(yīng)于可在.NET數(shù)據(jù)提供程序的連接字符串中使用的選項(xiàng)。例如SqlConnectionStringBuilder類(lèi)有DataSource屬性,用于指定數(shù)據(jù)源的位置,InitialCatalog屬性用于指定連接哪個(gè)數(shù)據(jù)庫(kù),IntegratedSecurity屬性用于指定使用何種方式連接。
Command對(duì)象可表示對(duì)數(shù)據(jù)庫(kù)的查詢,對(duì)存儲(chǔ)過(guò)程的調(diào)用,或者返回特定表內(nèi)容的直接請(qǐng)求。數(shù)據(jù)庫(kù)支持多種不同類(lèi)型的查詢。有些查詢通過(guò)引用一個(gè)表或多個(gè)表、視圖或者是通過(guò)調(diào)用一個(gè)存儲(chǔ)過(guò)程來(lái)獲取數(shù)據(jù)行,有些查詢則會(huì)對(duì)數(shù)據(jù)行進(jìn)行修改,還有一些查詢通過(guò)創(chuàng)建或修改諸如表、視圖或存儲(chǔ)過(guò)程對(duì)象來(lái)對(duì)數(shù)據(jù)庫(kù)的結(jié)構(gòu)進(jìn)行有關(guān)操作。Command對(duì)象都能夠支持。
DataReader用于以最快的速度檢索并檢查查詢所返回的行??墒褂肈ataReader對(duì)象來(lái)檢查查詢結(jié)果,一次檢查一行。當(dāng)移向下一行時(shí),前一行的內(nèi)容就會(huì)被丟棄。DataReader不支持更新操作。由DataReader返回的數(shù)據(jù)時(shí)只讀的。由于DataReader對(duì)象支持最小特性集,所以它的速度非產(chǎn)快。
有時(shí)可能希望將對(duì)數(shù)據(jù)庫(kù)所作的所有更改組織起來(lái),將它們看做一個(gè)獨(dú)立的工作單元。在數(shù)據(jù)庫(kù)編程中,這樣的工作單元就成為事務(wù)(Transacton)。假設(shè)某數(shù)據(jù)庫(kù)包含銀行的客戶信息,還有支票賬戶表和儲(chǔ)蓄賬戶表,一位用戶想要將錢(qián)從儲(chǔ)蓄賬戶轉(zhuǎn)到支票賬戶中去。在所編寫(xiě)的代碼中,希望確保儲(chǔ)蓄賬戶的提款操作和支票賬戶的存款操作是一個(gè)獨(dú)立的單元,要么同時(shí)成功完成,要么兩項(xiàng)操作都不發(fā)生,這就可以通過(guò)事務(wù)來(lái)實(shí)現(xiàn)。 Connection對(duì)象有一個(gè)BeginTransaction方法,可以用來(lái)創(chuàng)建Transactio對(duì)象。
在查詢語(yǔ)句中,我們希望通過(guò)Where子句來(lái)限定條件,這些條件,如果希望在運(yùn)行時(shí)動(dòng)態(tài)指定,那么我們可以通過(guò)Parameter對(duì)象來(lái)實(shí)現(xiàn)。我們可以通過(guò)為查詢中的所有參數(shù)創(chuàng)建Parameter對(duì)象,并將它們添加到Command對(duì)象的Parameter集合中。
就像上面那幅圖所示的,DataAdapter負(fù)責(zé)搬運(yùn)數(shù)據(jù)到DataSet中,DataAdapter對(duì)象的Fill方法提供了一種高效機(jī)制,用于將查詢結(jié)果引入DataSet或DataTable中,以便能夠脫機(jī)處理數(shù)據(jù)。還可以利用DataAdapter對(duì)象向數(shù)據(jù)庫(kù)提交存儲(chǔ)在DataSet對(duì)象中的掛起更改。 DataAdapter公開(kāi)了大量屬性,這些屬性實(shí)際上是Command對(duì)象。例如SelectCommand屬性包含一個(gè)Command對(duì)象,該對(duì)象表示將用來(lái)填充DataSet對(duì)象的查詢。此外,DataAdapter還有UpdateCommand、InsertCommand和DeleteCommand等屬性,這么做是為了允許開(kāi)發(fā)人員定義自己的更新邏輯。 二、非連接對(duì)象 1、DataTable類(lèi) ADO.NET的DataTable類(lèi)運(yùn)行我們通過(guò)行和列的集合來(lái)查看數(shù)據(jù)。可以通過(guò)DataAdapter對(duì)象的Fill方法將查詢結(jié)果存儲(chǔ)在DataTable中: Dim conn As SqlConnection '完成連接數(shù)據(jù)庫(kù)和填充數(shù)據(jù) conn = New SqlConnection("Data Source=(local);Initial Catalog=JWInfo;Integrated Security=True") dataSet = New DataSet() 在從數(shù)據(jù)庫(kù)中讀出數(shù)據(jù)并將其存儲(chǔ)在DataTable對(duì)象之后,該數(shù)據(jù)即從服務(wù)器斷開(kāi)連接。然后就可以脫機(jī)查看DataTable對(duì)象的內(nèi)容,而不會(huì)在ADO.NET和數(shù)據(jù)庫(kù)之間產(chǎn)生任何網(wǎng)絡(luò)通信流量。DataTable類(lèi)包含了其他非連接對(duì)象的集合,例如可以通過(guò)DataTable的Rows屬性訪問(wèn)其內(nèi)容,這一操作將返回DataRow對(duì)象的一個(gè)集合。如果希望查看DataTable表的結(jié)構(gòu),則可以使用其Columns屬性來(lái)獲取DataColumn的對(duì)象集合。DataTable還允許為該類(lèi)中存儲(chǔ)的數(shù)據(jù)定義一些約束,如主鍵??梢酝ㄟ^(guò)DataTable對(duì)象的Constrains屬性訪問(wèn)這些約束。 2、DataColumn類(lèi) 每個(gè)DataTable都有一個(gè)DataColum的集合,該集合時(shí)DataColum對(duì)象的容器,從其名稱(chēng)可以看出,一個(gè)DataColumn對(duì)象對(duì)應(yīng)于表中的一列,然而,DataColumn對(duì)象并非實(shí)際包含存儲(chǔ)在DataTable中的數(shù)據(jù),而是存儲(chǔ)了有關(guān)該列的結(jié)構(gòu)信息,例如數(shù)據(jù)類(lèi)型,是否是只讀的,是否允許存儲(chǔ)Null值,是否是唯一的等等。 DataColumn類(lèi)還包含一個(gè)Expression屬性,用來(lái)指定如何計(jì)算列中的數(shù)據(jù)。通常我們會(huì)通過(guò)表達(dá)式來(lái)計(jì)算查詢中一個(gè)列的值,例如: SELECT OrderID, ProductID, Quantity, UnitPrice, Quantity*UnitPrice AS ItemTotal From [Order Details] 這樣做的缺點(diǎn)是:數(shù)據(jù)庫(kù)引擎只會(huì)在查詢時(shí)執(zhí)行計(jì)算。如果修改了DataTable對(duì)象中的UnitPrice或者Quantity列的內(nèi)容,ItemTotal列不會(huì)發(fā)生任何變化。 ADO.NET的DataColumn類(lèi)定義了一個(gè)Expression屬性來(lái)完美地解決這個(gè)問(wèn)題。當(dāng)基于一個(gè)表達(dá)式來(lái)檢查DataColumn對(duì)象的值時(shí),ADO.NET會(huì)計(jì)算該表達(dá)式,并返回一個(gè)最新的值。這樣,如果更新了表達(dá)式中任一列的值,存儲(chǔ)在計(jì)算列中的值都是準(zhǔn)確的。下面是具體的使用方法: DataColumn col=new DataColumn(); col.ColumnName="ItemTotal"; col.DataType=typeof(Decimal); col.Expression="UnitPrice * Quantity"; 3、Constriant類(lèi) DataTable類(lèi)還提供了一種方式,用于對(duì)DataTable對(duì)象中本地存儲(chǔ)的數(shù)據(jù)設(shè)置約束。例如,可以建立一個(gè)Constraint對(duì)象,確保某一列或多列中的值在DataTable中式唯一的。Constraint對(duì)象存在于DataTable的Constraints集合中。 4、DataRow類(lèi) 要想訪問(wèn)存儲(chǔ)在DataTable對(duì)象中的實(shí)際值,可以使用此對(duì)象的Rows集合,該集合中包含一組DataRow對(duì)象。要想查看存儲(chǔ)在特定行、特定列中的數(shù)據(jù),可以使用對(duì)應(yīng)DataRow對(duì)象的Item屬性來(lái)讀取該行中任意列的值。DataRow的Item屬性有多個(gè)重載版本,可以通過(guò)指定列名稱(chēng),索引值,甚至DataColumn對(duì)象來(lái)明確要查看哪一列。由于Item是DataRow的默認(rèn)屬性,因此可以隱式地使用它: DataRow row; row=tbl.Rows[0]; Console.WriteLine(row[0]); Console.WriteLine(row["CustomerID"]); Console.WriteLine(row[MyTable.Columns["CustomerID"]]); 可以通過(guò)調(diào)用DataRow對(duì)象的BeginEdit方法,通過(guò)Item屬性修改此行中的一些列的值,然后通過(guò)EndEdit方法來(lái)講更改保存到改行中。通過(guò)調(diào)用DataRow的CancelEdit方法,可以取消在當(dāng)前編輯會(huì)話中所作的修改。 需要注意的是,在改變了一行的內(nèi)容時(shí),DataRow對(duì)象會(huì)緩存這些更改,從而可以在以后想數(shù)據(jù)庫(kù)提交他們。 5、DataSet類(lèi) 從其名稱(chēng)可以看出,DataSet對(duì)象包含一個(gè)數(shù)據(jù)集。可以將DataSet對(duì)象視為許多DataTable對(duì)象(它們存儲(chǔ)在DataSet對(duì)象的DataTables集合中)的容器,也就是說(shuō)相當(dāng)于一個(gè)內(nèi)存的數(shù)據(jù)庫(kù)。ADO.NET的目的是幫助開(kāi)發(fā)人員建立大型的多層數(shù)據(jù)庫(kù)應(yīng)用程序。有時(shí),開(kāi)發(fā)人員可能希望訪問(wèn)一個(gè)運(yùn)行在中間層服務(wù)器上的組件,以獲取許多表的內(nèi)容。這時(shí)不必重復(fù)調(diào)用該服務(wù)器以便每次從一個(gè)表中獲取數(shù)據(jù),而是可以將所有的數(shù)據(jù)都裝入一個(gè)DataSet對(duì)象中,并在一次單獨(dú)調(diào)用中將其返回。但DataSet對(duì)象的功能絕不僅僅作為多個(gè)DataTable對(duì)象的容器。 存儲(chǔ)在DataSet對(duì)象中的數(shù)據(jù)未與數(shù)據(jù)庫(kù)連接。對(duì)數(shù)據(jù)所作的任何修改都將只是緩存在每個(gè)DataRow中。要將這些更改傳遞給數(shù)據(jù)庫(kù)時(shí),將整個(gè)DataSet對(duì)象回傳給中間層服務(wù)器可能并非一種有效方法??梢允褂肎etChanges方法僅從DataSet中選出被修改的行。通過(guò)這樣的方式,可以在不同進(jìn)程或服務(wù)器之間傳遞較少的數(shù)據(jù)。 DataSet還公開(kāi)了一個(gè)Merge方法,該方法可以作為GetChanges方法的一個(gè)補(bǔ)充。用于向數(shù)據(jù)庫(kù)提交更改的中間層服務(wù)器(它使用的是由Merge方法返回的較小的DataSet)將會(huì)返回一個(gè)包含著新獲得數(shù)據(jù)的DataSet。可以使用DataSet類(lèi)的Merge方法來(lái)將兩個(gè)DataSet對(duì)象的內(nèi)容合并入一個(gè)DataSet中。 我們還可以在不建立與數(shù)據(jù)庫(kù)連接的情況下,就是用信息填充DataSet對(duì)象的Tables表集合。在以前的數(shù)據(jù)庫(kù)編程模型中,在本地添加新行之前,通常需要查詢數(shù)據(jù)庫(kù),然后將它們提交給數(shù)據(jù)庫(kù)。而使用ADO.NET,在準(zhǔn)備好提交數(shù)據(jù)行之后,才需要與數(shù)據(jù)庫(kù)通信。 6、DataRelation類(lèi) 數(shù)據(jù)庫(kù)中的表,通常以某種方式相關(guān)聯(lián)。例如,在Northwind數(shù)據(jù)庫(kù)中,Orders表中的每個(gè)項(xiàng)都與Customers表的一個(gè)表項(xiàng)相關(guān)聯(lián),因此可以確定哪位客戶下了哪些訂單。ADO.NET的DataRelation對(duì)象借助于DataRelation類(lèi)來(lái)處理來(lái)自相關(guān)DataTable對(duì)象的數(shù)據(jù)。 DataTable類(lèi)公開(kāi)了一個(gè)Relations屬性,該屬性是DataRelation對(duì)象的一個(gè)集合。可以使用DataRelation對(duì)象來(lái)表示DataSet中不同DataTable對(duì)象之間的關(guān)系。 DataSet ds; DataTable tblCustomers,tblOrders; DataRelation rel; //……創(chuàng)建并初始化DataSet rel=ds.Relations.Add("Customers_Orders",tblCustomers.Columns["CustomerID"],tblOrders.Columns["CustomerID"]); foreach(DataRow rowCustomer in tblCustomers.Rows) { Console.WriteLine(rowCustomer["CompanyName"]); foreach(DataRow rowOrder in rowCustomer.GetChildRows(rel)) Console.WriteLine("{0}",rowOrder["OrderID"]); Console.WriteLine(); } 7、DataView類(lèi) 在將一查詢結(jié)果置于DataTable對(duì)象中以后,就可以使用DataView對(duì)象以不同的方式來(lái)查看數(shù)據(jù)。如果希望根據(jù)某一列對(duì)DataTable對(duì)象的內(nèi)容進(jìn)行排序,只需要將DataView的Sort屬性設(shè)置為該列的名稱(chēng)即可。還可以設(shè)置DataView的Filter屬性,使得只有符合特定標(biāo)準(zhǔn)的行可見(jiàn)。 可以使用多個(gè)DataView對(duì)象同時(shí)查看同一個(gè)DataTable。例如在一個(gè)窗體中可以擁有兩個(gè)表格,其中一個(gè)用于按照字母順序顯示所有客戶,另一個(gè)則按照國(guó)家進(jìn)行排序。為了顯示所有視圖,需要將每個(gè)表格綁定到不同的DataView對(duì)象。 |
|