Loading AI tools
来自维基百科,自由的百科全书
ADO.NET是微軟在.NET Framework中負責資料存取的類別庫集,它是使用在COM時代奠基的OLE DB技術以及.NET Framework的類別庫和程式語言來發展的,它可以讓.NET上的任何程式語言能夠連接並存取關聯式資料庫與非資料庫型資料來源(例如XML,Excel或是文字檔資料),或是獨立出來作為處理應用程式資料的類別物件,其在.NET Framework中的地位是舉足輕重,許多人將ADO.NET視為 ActiveX Data Objects (ADO) 的下一個版本,但其實它是一個全新的架構、產品與概念。
作業系統 | Microsoft Windows |
---|---|
類型 | 軟件框架 |
許可協定 | MS-EULA、BCL(在微軟參考授權下發佈) |
網站 | ADO.NET Overview on MSDN |
ADO.NET類封裝在System.Data.dll中,並且與System.Xml.dll中的XML類整合。
在1990年代初期,微軟已經開發了許多的資料存取方式,像是ODBC架構、和Microsoft Access資料庫互動使用的DAO物件、可以跨越網絡存取資料的RDO以及讓DAO元件可以存取ODBC資料來源的ODBCDirect技術等等,技術雖然多,但是卻又各自為政,而且每個技術的重疊性也很高(像是ODBC有Microsoft Access的驅動程式);RDO雖然可跨網絡,但是ODBC的驅動程式中也有提供跨網絡的功能(像是SQL Server和Oracle驅動程式),如此琳琅滿目重疊性又高的技術群,讓企業與開發人員在選擇、學習與應用上產生了很多的困難。
1996年,適逢COM的出現,微軟將資料存取的核心開始改寫為以COM為基礎的OLE DB,並且在它上面建立一個新的統一的資料存取的高層對象模型-ADO。ADO推出後順利的取代了DAO和RDO,成為在Windows NT 4.0和Windows 2000作業系統上開發資料庫應用程式的首選。它將OLE DB的物件模型進一步簡化;由資料庫廠商開發滿足OLE DB介面的資料提供者(data provider,這個模式在此時奠基),而ADO本身則是與資料來源無關(data source independent)的對象結構,讓它迅速的獲得了使用ASP、Visual Basic、COM的開發人員的青睞。它能夠順利取代DAO與RDO,要關鍵在於ADO的與資料庫伺服器端/客戶端的特性無關,這使得ADO通用性極好。然而ADO本身的架構仍然有缺陷(尤其是在開發網絡應用程式時,最好的例子就是Recordset無法離線),這也是微軟為何不在.NET Framework中繼續使用ADO的主要原因。
1998年時,微軟提出了一個下一代的應用程式開發框架(Application Framework)的計劃[1],計劃中包含了:
ADO+即為Storage+的一支。
1998年起,因為Web應用程式的竄起,大大改變了許多應用程式的設計方式,傳統的資料庫連線儲存設計法無法適用於此類應用程式,這讓ADO應用程式遇到了很大的瓶頸,也讓微軟開始思考讓資料集(Resultset,在ADO中稱為Recordset)能夠離線化的能力,以及能在用戶端建立一個小型資料庫的概念[2][註 1],這個概念就是ADO.NET中離線型資料模型(disconnected data model)的基礎,而在ADO的使用情形來看,資料庫連線以及資源耗用的情形較嚴重(像是Server-side cursor或是Recordset.Open會保持連線狀態),在ADO.NET中也改良了這些物件,構成了能夠減少資料庫連線和資源使用量的功能。XML的使用也是這個版本的重要發展之一。
2000年,微軟的Microsoft .NET計劃開始成形,許多的微軟產品都冠上.NET的標籤,ADO+也不例外,改名為ADO.NET[註 2],並包裝到.NET Framework類別庫中,成為.NET平台中唯一的資料存取元件。
ADO.NET由連線資料來源(connected data source)以及離線資料模型(disconnected data model)兩個部份構成[3],這兩個部份是相輔相成的。
若沒辦法連線到資料庫,則無法被稱為資料存取元件。連線資料來源便是用來連接資料庫(或是具有OLE DB資料來源提供者)的物件類別[4],由下列介面構成:
Open()/Close()
:開啟與關閉資料庫連線。BeginTransaction()
:啟動資料庫交易,並回傳一個IDbTransaction
物件,以控制交易的結果。ExecuteNonQuery()
:執行不回傳結果集的資料庫指令,像是INSERT
、UPDATE
與DELETE
指令,返回值為該命令所影響的行數。 對於其他所有類型的陳述式,返回值 為 -1。ExecuteScalar()
:執行指令並回傳第一列第一行中的值(object類型)。當沒有資料時,ExcuteScalar方法返回System.DBNull。ExecuteReader()
:執行指令並回傳IDataReader物件,以讀取資料集中的資料。BeginExecuteNonQuery
:開始執行非同步查詢EndExecuteNonQuery
: 結束執行非同步查詢Commit()
:認可資料庫交易。Rollback()
:復原資料庫交易。Fill()
:將資料填入離線型資料物件。Update()
:將變更過的離線型資料物件中的資料寫回資料庫。IDbDataAdapter
內部也是由它來讀取資料。
Read()
:第一次呼叫Read()方法取得第一行資料,並將游標指向下一行資料。當再次呼叫該方法時候,將讀取下一行資料。當檢測到不再有資料行時,Read()方法將返回false
。IDataReader
讀取資料後實際裝載資料列的物件,提供方法來讀取資料行中的資料,以及轉換成.NET Framework原生型別的工具。
GetOrdinal()
:取得指定資料行的欄位索引值。IsDBNull()
:判斷指定欄位的資料是否為NULL值。使用連線資料來源需要由開發人員自我管理連線,並且直接操作資料存取的相關細節,但它的優點是速度快,而且可以自訂整個資料存取流程的邏輯。
在.NET Framework中,ADO.NET預設提供了四種資料來源:
其他廠商亦為不同的資料庫提供資料來源:
對每種Data Provider,ADO.NET要實現下述對象結構:
例如,對於SQL Server資料源:
strSQL = "SELECT * FROM users WHERE Name = @Name and Password = @Password";
SqlParamter[] paras = new SqlParamter[]{//参数数组
new SqlParamter("@Name",SqlDBType.Varchar,50)
new SqlParamter("@Password",SqlDBType.Varchar,50)};
paras[0].value = userName;//绑定用户名
paras[1].value = password;//绑定用户密码
離線資料模型是微軟為了改良ADO在網絡應用程式中的缺陷所設計的,同時它也是COM+中,IMDB技術的設計概念的實作品,但它並沒有完整的IMDB功能,像是交易處理(transaction processing),但它仍不失為一個能在離線狀態下處理資料的好幫手,它也可以透過連線資料來源物件,支援將離線資料存回資料庫的能力[5]。離線資料模型由下列物件組成:
ReadXml()/WriteXml()
:以DataSet的結構讀寫XML。ReadXmlSchema()/WriteXmlSchema()
:以DataSet的結構讀寫XML Schema。GetXml()/GetXmlSchema()
:取得DataSet內容的XML或XML Schema。Merge()
:合併兩個DataSet。Load()
:自IDataReader
載入資料到DataSet。AcceptChanges()
:將修改過的資料列的修改旗標改為Unchanged
。GetChanges()
:將修改過的資料列以DataRow陣列方式傳回。RejectChanges()
:復原所有資料的修改。Copy()
:將DataTable複製出一個副本,包含結構與資料。Merge()
:將兩個DataTable合併。Select()
:以指定的特殊查詢語法,傳回符合條件的DataRow陣列。Compute()
:以指定的彙總語法,傳回彙總的結果。GetErrors()
:傳回有錯誤的DataRow陣列。HasErrors
:判斷DataTable中的DataRow有沒有含有錯誤的DataRow。IsNull()
:判斷指定的欄位是否為NULL值。ItemArray
:將DataRow中的資料轉換成陣列。Filter
:設置DataView的過濾條件。Sort
:設置DataView的排序條件。ToTable()
:將套用過濾與排序後的內容轉換為DataTable物件。ForeignKeyConstraint
為外部鍵限制,而UniqueConstraint
則確保了欄位中的值都是唯一的。DataSet和DataTable除了資料庫的處理以外,也經常被用來管理應用程式中的資料,並且由於它可以儲存在XML中的特性,也讓它可以用來儲存需要儲存的應用程式資訊。DataSet中可包含DataRelation對象,用於建構表之間的約束關係。
在.NET Framework 1.x的時代,ADO.NET不同的來源有不同的類別搭配(前面已述及),但是若想要在不同的資料來源間搭配,那麼勢必要產生很多的變數來存放不同的資料物件,因此微軟在.NET Framework 2.0中提供了一個System.Data.Common
命名空間,其中有各種必要物件的共用方法(例如連線是DbConnection
,命令是DbCommand
,讀取器是DbDataReader
,資料配接器是DbDataAdapter
等),以及DbProviderFactory
物件,用來總管資料存取的物件。
DbProviderFactories
則是電腦中所有提供者的總管,開發人員可用DbProviderFactories.GetFactoryClasses()
取得各個提供者的Invariant Name,再於呼叫DbProviderFactories.GetFactory()
時傳入指定提供者的Invariant Name即可取得DbProviderFactory
,再利用下列方法取得共用物件:
DbProviderFactory.CreateConnection()
DbProviderFactory.CreateCommand()
DbProviderFactory.CreateParameter()
DbProviderFactory.CreateDataAdapter()
// This example assumes a reference to System.Data.Common.
static DataTable GetProviderFactoryClasses()
{
// Retrieve the installed providers and factories.
DataTable table = DbProviderFactories.GetFactoryClasses();
// Display each row and column value.
foreach(DataRow row in table.Rows)
{
foreach(DataColumn column in table.Columns)
{
Console.WriteLine(row[column]);
}
}
return table;
}
XML在ADO.NET中扮演了相當重要的地位,DataSet和DataTable都可以轉換成XML或和XML之間交換資料,在DataTable的內部資料的變更記錄,可以被輸出到一個XML的格式,用來識別變更的情形,這個格式稱為DiffGram,而且它可以直接讀入DataTable之中(使用DataTable.ReadXml()並用XmlReadMode.DiffGram當參數)。一個典型的DiffGram如下:
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
<CustomerDataSet>
<Customers diffgr:id="Customers1" msdata:rowOrder="0" diffgr:hasChanges="modified">
<CustomerID>ALFKI</CustomerID>
<CompanyName>New Company</CompanyName>
</Customers>
<Customers diffgr:id="Customers2" msdata:rowOrder="1" diffgram:hasErrors="true">
<CustomerID>ANATR</CustomerID>
<CompanyName>Ana Trujillo Emparedados y Helados</CompanyName>
</Customers>
<Customers diffgr:id="Customers3" msdata:rowOrder="2">
<CustomerID>ANTON</CustomerID>
<CompanyName>Antonio Moreno Taquera</CompanyName>
</Customers>
<Customers diffgr:id="Customers4" msdata:rowOrder="3">
<CustomerID>AROUT</CustomerID>
<CompanyName>Around the Horn</CompanyName>
</Customers>
</CustomerDataSet>
<diffgr:before>
<Customers diffgr:id="Customers1" msdata:rowOrder="0">
<CustomerID>ALFKI</CustomerID>
<CompanyName>Alfreds Futterkiste</CompanyName>
</Customers>
</diffgr:before>
<diffgr:errors>
<Customers diffgr:id="Customers2" diffgr:Error="An optimistic concurrency violation has occurred for this row."/>
</diffgr:errors>
</diffgr:diffgram>
DataSet與DataTable也支援直接讀入XML Schema建立結構的能力,以及自行依XML的內容推斷(inference)其結構的能力,下列程式碼為由XML推斷結構的程式:
DataSet dataSet = new DataSet();
dataSet.InferXmlSchema9("input_od.xml", new string[] "urn:schemas-microsoft-com:officedata");
DataSet和DataTable可以使用XmlDataDocument類別和XML DOM整合在一起,XmlDataDocument的角色就像一個橋接介面,並且作為DataSet和DataTable可使用XPath與XML DOM方式存取的方法。下列程式碼即為使用XmlDataDocument和資料庫中資料轉換為XSLT輸出的範例:
// Assumes connection is a valid SqlConnection.
connection.Open();
DataSet custDS = new DataSet("CustomerDataSet");
SqlDataAdapter customerAdapter = new SqlDataAdapter(
"SELECT * FROM Customers", connection);
customerAdapter.Fill(custDS, "Customers");
SqlDataAdapter orderAdapter = new SqlDataAdapter(
"SELECT * FROM Orders", connection);
orderAdapter.Fill(custDS, "Orders");
connection.Close();
custDS.Relations.Add("CustOrders",
custDS.Tables["Customers"].Columns["CustomerID"],
custDS.Tables["Orders"].Columns["CustomerID"]).Nested = true;
XmlDataDocument xmlDoc = new XmlDataDocument(custDS);
XslTransform xslTran = new XslTransform();
xslTran.Load("transform.xsl");
XmlTextWriter writer = new XmlTextWriter("xslt_output.html",
System.Text.Encoding.UTF8);
xslTran.Transform(xmlDoc, null, writer);
writer.Close();
在.NET Framework中,DataSet被分為兩類,一種是不會強制使用特別型態的DataSet,稱為Untyped DataSet,使用上較方便,但沒有強制的型別限制,另一種則是Typed DataSet,會強制型別,並且是由自訂的XML Schema所產生,Untyped DataSet則沒有XML Schema,由建立時的結構來決定,Typed DataSet可以用Visual Studio,或者是SDK工具中的xsd.exe來產生。
xsd.exe /d /l:CS XSDSchemaFileName.xsd /eld /n:XSDSchema.Namespace
產生出來的Typed DataSet會自動將欄位設置成屬性,讓開發人員的存取更方便(這個功能在TableAdapter相當常見)。
CustomerDataSet customers = new CustomerDataSet();
SqlDataAdapter adapter = new SqlDataAdapter(
"SELECT * FROM dbo.Customers;",
"Data Source=(local);Integrated " +
"Security=SSPI;Initial Catalog=Northwind");
adapter.Fill(customers, "Customers");
foreach(CustomerDataSet.CustomersRow customerRow in customers.Customers)
Console.WriteLine(customerRow.CustomerID);
ADO.NET中有專門用來產生資料處理指令的指令產生器(Command Builder),它可以利用開發人員所指定的SELECT
指令,自動產生對應的INSERT
、UPDATE
與DELETE
指令,但一開始它並不會自動產生,而是要靠呼叫方法來取得:
DbCommandBuilder.GetInsertCommand()
DbCommandBuilder.GetUpdateCommand()
DbCommandBuilder.GetDeleteCommand()
最常使用到的地方是和DataAdapter並用時,但它要求傳入的SELECT
陳述式所選擇的列集合中必須要有主鍵或者唯一鍵[6],否則無法產生,同時自動產生的指令因為判斷條件很多,對效能可能會有些影響。
ADO.NET和Visual Studio開發工具幾乎已經是無縫的整合了,開發人員可以利用Visual Studio來建立強型別(strong-typed)的DataSet,到了Visual Studio 2005時更能夠在Windows Forms應用程式中使用TableAdapter(Typed DataSet和DataAdapter整合的產物)來開發應用程式(不會再看到DataAdapter,但使用上差不多)[7]。Visual Studio在建立Typed DataSet時有提供視覺化介面的支援,以及資料庫組態精靈(Database Configuration Wizard)來讓開發人員以簡單的設置方式來建立DataSet,部份開發人員也將TableAdapter和ASP.NET應用程式的ObjectDataSource控制項並用,亦得到不錯的效果。
在.NET Framework 3.5中,微軟特別為了DataSet和DataTable建立了LINQ Provider(稱為LINQ to DataSet或LINQ to ADO.NET),讓LINQ可以在DataSet或DataTable上使用,可以讓原本在DataSet上的投資(程式碼)得以繼續使用並享有LINQ的便利性。
對於ADO的開發人員來說,最明顯的變化在於以往ADO中的Recordset消失了,並且明確的分開為連線型的DataReader以及離線型的DataSet與DataTable,並且發展支援離線型資料來源的瀏覽工具DataView[8],這樣的改變,讓習慣使用ADO的VB/ASP開發人員會有某種程度的不習慣,同時讓ADO.NET的學習會較ADO有較些許的複雜性,因此有部份新入門或是VB 6.0/ASP開發人員會在學習.NET Framework或是使用VB.NET開發應用程式時,在.NET Framework中使用ADO來連接資料來源。但在.NET Framework應用程式使用ADO的話,.NET Framework會因為要多一層ADO的COM與VB.NET使用的.NET之間的資料轉換,會讓應用程式效能有少部份的損耗[9]。
下述範例,用ADO讀取一個資料庫中的表格,然後轉為ADO.Net的資料,最後繫結到DataGridView控制項中去顯示:
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
cn = New ADODB.Connection
rs = New ADODB.Recordset
cn.Provider = "Microsoft.ACE.OLEDB.12.0;"
cn.ConnectionString = "D:\\budget2016.accdb;"
cn.Open()
'check whether the serial num exists in table cutter
Dim sSQL As String = "SELECT * FROM tPassword"
rs.CursorLocation = ADODB.CursorLocationEnum.adUseClient
rs.Open(sSQL, cn, ADODB.CursorTypeEnum.adOpenKeyset, ADODB.LockTypeEnum.adLockReadOnly,_
ADODB.CommandTypeEnum.adCmdText)
Dim da As New System.Data.OleDb.OleDbDataAdapter()
Dim ds As New DataSet()
da.Fill(ds, rs, "tPassword")
'rs.Close() 'Cannot close
rs.ActiveConnection = Nothing
cn.Close()
rs = Nothing
cn = Nothing
DataGridView1.DataSource = (ds.Tables("tPassword"))
ADO的RecordSet,相當於在客戶機或伺服器的一個邏輯上的資料表格,使用者可以通過cursor來定位/讀寫當前行,但不能重新對這些行重新排序,也不能對客戶機上的多個RecordSet資料執行跨表的連接(join)查詢。ADO.Net針對上述缺點,在客戶機上實現了DataTable與DataSet,這是客戶機主記憶體中的一套(多個)資料表格,可以對其執行各種單表或多表的查詢、修改、刪除操作,既可以使用SQL陳述式,也可以使用LINQ子語言。
ADO與ADO.Net的共同點,在其內部都是使用SQL陳述式來對後台資料庫操作。
內在實現上,ADO是基於COM技術,因此變體類型(Variant)無處不在,這是一種通用、萬能類型;而ADO.Net是強型別的,並很好地支援資料庫元數據與XML功能。
隨着網絡應用程式的進化,ADO.NET也隨之做了許多的改變,但不變的是,ADO.NET的基礎提供了強固的發展支援,這些進化的技術都是植基於ADO.NET的核心元件而來。
長久以來,程式設計師和資料庫總是保持着一種微妙的關係,在商用應用程式中,資料庫一定是不可或缺的元件,這讓程式設計師一定要為了連接與存取資料庫而去學習SQL指令,因此在資訊業中有很多人都在研究如何將程式設計模型和資料庫整合在一起,物件關聯對應(Object-Relational Mapping)的技術就是由此而生,像Hibernate或NHibernate都是這個技術下的產物,而微軟雖然有了ADO.NET這個資料存取的利器,但卻沒有像NHibernate這樣的物件對應工具,因此微軟在.NET Framework 2.0發展時期,就提出了一個ObjectSpace的概念,ObjectSpace可以讓應用程式可以用完全物件化的方法連接與存取資料庫,其技術概念與NHibernate相當類似,然而ObjectSpace專案相當大,在.NET Framework 2.0完成時仍無法全部完成[10],因此微軟將ObjectSpace納入下一版本的.NET Framework中,並且再加上一個設計的工具(Designer),構成了現在的ADO.NET Entity Framework。
Entity Framework利用了抽象化資料結構的方式,將每個資料庫物件都轉換成應用程式物件(entity),而資料欄位都轉換為屬性(property),關聯則轉換為結合屬性(association),讓資料庫的E/R模型完全的轉成物件模型,如此讓程式設計師能用最熟悉的程式語言來呼叫存取。而在抽象化的結構之下,則是高度整合與對應結構的概念層、對應層和儲存層,以及支援Entity Framework的資料提供者(provider),讓資料存取的工作得以順利與完整的進行。
以往在發展像是AJAX應用程式時,伺服端總是需要設計一個HTTP介面埠(end point),通常都會使用Web Service來實作,但是隨着Mashup應用程式的成長,若每次都要為一份(或一組)資料撰寫Web Service或HTTP end point的話,對開發人員也是不小的負擔,而且Web Service只支援XML/SOAP的資料格式,無法相容於Mashup應用程式常用的JSON資料格式,微軟也發現未來的Silverlight應用程式也是會面臨到相同問題。
當時剛好微軟的ADO.NET Entity Framework也正在開發中,它的EDM能力剛好可以提供給WCF資料存取的能力,因此微軟特別以ADO.NET Entity Framework為基礎,開發一個專門提供HTTP端點資料服務的資料供應層,即為WCF Data Services。
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.