ADO.NET
操作系统Microsoft Windows
类型软件框架
许可协议MS-EULABCL(在微软参考授权下发布)
网站ADO.NET Overview on MSDN

ADO.NET是微軟在.NET Framework中負責資料存取的類別庫集,它是使用在COM時代奠基的OLE DB技術以及.NET Framework的類別庫和程式語言來發展的,它可以讓.NET上的任何程式語言能夠連接並存取關聯式資料庫與非資料庫型資料來源(例如XML,Excel或是文字檔資料),或是獨立出來作為處理應用程式資料的類別物件,其在.NET Framework中的地位是舉足輕重,許多人將ADO.NET視為 ActiveX Data Objects (ADO) 的下一個版本,但其實它是一個全新的架構、產品與概念。

ADO.NET类封装在System.Data.dll中,并且与System.Xml.dll中的XML类集成。

历史

發展緣起

在1990年代初期,微軟已經開發了許多的資料存取方式,像是ODBC架構、和Microsoft Access資料庫交互使用的DAO物件、可以跨越網路存取資料的RDO英语Remote Data Objects以及讓DAO元件可以存取ODBC資料來源的ODBCDirect技術等等,技術雖然多,但是卻又各自為政,而且每個技術的重疊性也很高(像是ODBC有Microsoft Access的驅動程式);RDO雖然可跨網路,但是ODBC的驅動程式中也有提供跨網路的功能(像是SQL ServerOracle驅動程式),如此琳琅滿目重疊性又高的技術群,讓企業與開發人員在選擇、學習與應用上產生了很多的困難。

1996年,適逢COM的出现,微軟將資料存取的核心開始改寫為以COM為基础的OLE DB,並且在它上面建立一個新的统一的資料存取的高层对象模型-ADO。ADO推出後順利的取代了DAO和RDO,成為在Windows NT 4.0Windows 2000作業系統上開發資料庫應用程式的首選。它將OLE DB的物件模型进一步简化;由資料庫廠商开发满足OLE DB接口的資料提供者(data provider,這個模式在此時奠基),而ADO本身則是與資料來源無關(data source independent)的对象结构,讓它迅速的獲得了使用ASPVisual BasicCOM的開發人員的青睞。它能夠順利取代DAO與RDO,要關鍵在于ADO的与数据库服务器端/客户端的特性无关,这使得ADO通用性极好。然而ADO本身的架構仍然有缺陷(尤其是在開發網路應用程式時,最好的例子就是Recordset無法離線),這也是微軟為何不在.NET Framework中繼續使用ADO的主要原因。

1998年時,微軟提出了一個下一代的應用程式開發框架(Application Framework)的計畫[1],計畫中包含了:

ADO+即為Storage+的一支。

ADO.NET的前身:ADO+

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],由下列接口構成:

使用連線資料來源需要由開發人員自我管理連線,並且直接操作資料存取的相關細節,但它的優點是速度快,而且可以自訂整個資料存取流程的邏輯。


ADO.NET資料提供者(Data Provider)

在.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]。離線資料模型由下列物件組成:

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,再利用下列方法取得共用物件:

// 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的整合

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指令,自動產生對應的INSERTUPDATEDELETE指令,但一開始它並不會自動產生,而是要靠呼叫方法來取得:

最常使用到的地方是和DataAdapter並用時,但它要求傳入的SELECT语句所选择的列集合中必須要有主键或者唯一键[6],否則無法產生,同時自動產生的指令因為判斷條件很多,對效能可能會有些影响。

Visual Studio的支援

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.NET和ADO的差異

對於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的基礎提供了強固的發展支援,這些進化的技術都是植基於ADO.NET的核心元件而來。

主条目:Entity Framework

長久以來,程式設計師和資料庫總是保持著一種微妙的關係,在商用應用程式中,資料庫一定是不可或缺的元件,這讓程式設計師一定要為了連接與存取資料庫而去學習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),讓資料存取的工作得以順利與完整的進行。

主条目:WCF Data Services

以往在發展像是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。

注释

  1. ^ 此概念的原型為In-Memory Database,又稱IMDB,可在記憶體中運行的資料庫,然而以當時的環境(2000年初,記憶體尚未跌到目前的價格水準),以及技術不成熟的情況下,這項技術在Windows 2000 RC2時被抽離。
  2. ^ 有相同遭遇的還有ASP+(改名為ASP.NET)。

参考文献

  1. ^ COM+, A Windows 2000 technology showcase. [2009-01-11]. (原始内容存档于2000-08-16). 
  2. ^ ADO+. [2009-01-11]. (原始内容存档于2009-12-03). 
  3. ^ ADO.NET架構. [2009-01-11]. (原始内容存档于2010-01-19). 
  4. ^ 擷取和修改ADO.NET中的資料. [2009-01-11]. (原始内容存档于2008-12-02). 
  5. ^ DataSet、DataTable及DataView(ADO.NET). [2009-01-11]. (原始内容存档于2008-09-26). 
  6. ^ .NET Framework开发人员指南 - 使用CommandBuilder生成命令(ADO.NET). [2009-03-18]. (原始内容存档于2009-03-19). 
  7. ^ TableAdapter. [2009-01-11]. (原始内容存档于2009-09-01). 
  8. ^ ADO.NET ─ ADO開發人員指引. [2006-04-29]. (原始内容存档于2006-01-11). 
  9. ^ Revisiting the Use of ADO in .NET Applications. [2009-01-11]. (原始内容存档于2009-01-13). 
  10. ^ A First Look at ObjectSpaces in Visual Studio 2005. [2008-08-17]. (原始内容存档于2008-09-05). 

参见