ADO.NET


ADO.NET (正體)

跳过字词转换说明

ADO.NET是微软在.NET Framework中负责数据访问的类库集,它是使用在COM时代奠基的OLE DB技术以及.NET Framework的类库和编程语言来发展的,它可以让.NET上的任何编程语言能够连接并访问关系数据库与非数据库型数据来源(例如XML,Excel或是文字档数据),或是独立出来作为处理应用程序数据的类对象,其在.NET Framework中的地位是举足轻重,许多人将ADO.NET视为ADO的下一个版本,但其实它是一个全新的架构、产品与概念。

目录

发展缘起

1997年时,微软已经开发了许多的数据访问方式,像是ODBC架构、和Microsoft Access数据库交互使用的DAO对象、可以跨越网络访问数据的RDO以及让DAO组件可以访问ODBC数据来源的ODBCDirect技术等等,技术虽然多,但是却又各自为政,而且每个技术的重叠性也很高(像是ODBC有Microsoft Access的驱动程序),RDO虽然可跨网络,但是ODBC的驱动程序中也有提供跨网络的功能(像是SQL ServerOracle驱动程序),如此琳琅满目重叠性又高的技术群,让企业与开发人员在选择、学习与应用上产生了很多的困难。同时适逢 COMOLE的发展,微软将数据访问的内核开始改写为以COM为主的OLE DB,并且在它上面创建一个新的数据访问模型-ADO

ADO推出后顺利的取代了DAO和RDO,成为在Windows NT 4.0Windows 2000操作系统上开发数据库应用程序的首选,除了它将对象模型统一化,改由数据库厂商发展数据提供者(data provider,这个模式在此时奠基),而ADO本身则是与数据来源无关 (data source independent) 的开发方法,让它迅速的获得了使用ASPVisual Basic开发人员的青睐,也是它能够顺利取代DAO与RDO等模型的主要关键。然而ADO本身的架构仍然有缺陷(尤其是在开发网络应用程序时,最好的例子就是Recordset无法脱机),这也是微软为何不在.NET Framework中继续使用ADO的主要原因,在1998年时,微软提出了一个下一代的应用程序开发幀架 (Application Framework) 的计划[1],计划中包含了:

  • ASP+:改良与重新设计ASP技术,强化它的Web应用程序发展能力。
  • Storage+:发展新的数据库与面向对象之文件系统结构(用于 SQL Server 8.0(即后来的SQL Server 2000)与NTFS),以及发展新一代的数据访问组件,并改良ADO本身的缺陷,让它更能够成为应用程序数据访问的内核功能。
  • COM+:改良 COM 和 MTS,成为企业级应用程序开发的基础组件。

ADO+即为Storage+的一支。

ADO.NET的前身:ADO+

1998年起,因为Web应用程序的窜起,大大改变了许多应用程序的设计方式,传统的数据库连接保存设计法无法适用于此类应用程序,这让ADO应用程序遇到了很大的瓶颈,也让微软开始思考让数据集(Resultset,在ADO中称为Recordset)能够脱机化的能力,以及能在用户端创建一个小型数据库的概念[2][3],这个概念就是ADO.NET中脱机型数据模型 (disconnected data model) 的基础,而在ADO的使用情形来看,数据库连接以及资源耗用的情形较严重(像是 Server-side cursor 或是 Recordset.Open 会保持连接状态),在ADO.NET中也改良了这些对象,构成了能够减少数据库连接和资源使用量的功能。XML的使用也是这个版本的重要发展之一。

2000年,微软的Microsoft .NET计划开始成形,许多的微软产品都冠上.NET的标签,ADO+也不例外,改名为ADO.NET,[4]并包装到.NET Framework类库中,成为.NET平台中唯一的数据访问组件。

架构

ADO.NET 由连接数据来源 (connected data source) 以及脱机数据模型 (disconnected data model) 两个部分构成[5],这两个部分是相辅相成的,同时依照接口的不同,分为:

  • SQL Server 本地数据来源-System.Data.SqlClient(以 Sql 为字首的类群)。
  • OLE DB 数据来源-System.Data.OleDb(以 OleDb 为字首的类群)。
  • Oracle 数据来源-System.Data.OracleClient(以 Oracle 为字首的类群)。
  • ODBC 数据来源-System.Data.Odbc(以 Odbc 为字首的类群)。

连接数据来源

若没办法连接到数据库,则无法被称为数据访问组件。连接数据来源便是用来连接数据库(或是具有 OLE DB 数据来源提供者)的对象类[6],由下列类所构成:

  • IDbConnection,负责与数据库的连接管理,包含连接字符串 (connection string),连接的开关,数据库交易的启始与连接错误的处理,所有的 ADO.NET 数据提供者都要实现此接口。
    • Open()/Close():打开与关闭数据库连接。
    • BeginTransaction():激活数据库交易,并回传一个 IDbTransaction 对象,以控制交易的结果。
  • IDbCommand,负责运行数据库指令(在大多数的案例中都是SQL指令),并传回由数据库中截取的结果集,或是运行不回传结果集的数据库指令。
    • ExecuteNonQuery():运行不回传结果集的数据库指令,像是INSERTUPDATEDELETE指令。
    • ExecuteScalar():运行指令并回传第一列第一行中的数据。
    • ExecuteReader():运行指令并回传 IDataReader 对象,以读取数据集中的数据。
  • IDataParameter,负责装载数据库指令所需要的参数数据,在使用参数化查询时会经常使用。
  • IDbTransaction,负责装载数据库交易所需的控制对象,以运行交易的认可 (commit) 或撤销 (rollback) 的工作。
    • Commit():认可数据库交易。
    • Rollback():撤销数据库交易。
  • IDbDataAdapter,负责将来自于 IDbCommand 运行取得的结果集,装载到脱机型数据集 (DataSet) 或是脱机型数据表 (DataTable) 中。
    • Fill():將資料填入離線型資料物件。
    • Update():将变更过的脱机型数据对象中的数据写回数据库。
  • IDataReader,创建一个只可向前读取光标 (forward-only) 的数据读取器工具,以逐列读取方式访问数据,IDbDataAdapter内部也是由它来读取数据。
    • Read():读取下一列,开发人员利用此方法移动数据集中的光标,若数据集中的数据列已读取完毕时,传回 false
  • IDataRecord,在 IDataReader 读取数据后实际装载数据列的对象,提供方法来读取数据行中的数据,以及转换成.NET Framework本地型别的工具。
    • GetOrdinal():取得指定数据行的字段索引值。
    • IsDBNull():判断指定字段的数据是否为NULL值

使用连接数据来源需要由开发人员自我管理连接,并且直接操作数据访问的相关细节,但它的优点是速度快,而且可以自定义整个数据访问流程的逻辑。

脱机数据模型

脱机数据模型是微软为了改良ADO在网络应用程序中的缺陷所设计的,同时它也是COM+中,IMDB技术的设计概念的实现品,但它并没有完整的IMDB功能,像是交易处理 (transaction processing),但它仍不失为一个能在脱机状态下处理数据的好帮手,它也可以通过连接数据来源对象,支持将脱机数据存回数据库的能力[7]。脱机数据模型由下列对象组成:

  • DataSet,脱机型数据模型的内核之一,可将它当成一个脱机型的数据库,它可以内含许多个 DataTable,并且利用关系与限制方式来设置数据的完整性,它本身也提供了可以和 XML 交互作业的支持。
    • 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():撤销所有数据的修改。
  • DataTable,脱机型数据模型的内核之一,可将它当成一个脱机型的数据表,是存储数据的收纳器。
    • Copy():将 DataTable 复制出一个副本,包含结构与数据。
    • Merge():将两个 DataTable 合并。
    • Select():以指定的特殊查询语法,传回符合条件的 DataRow 数组。
    • Compute():以指定的汇总语法,传回汇总的结果。
    • GetErrors():传回有错误的 DataRow 数组。
    • HasErrors:判断 DataTable 中的 DataRow 有没有含有错误的 DataRow。
  • DataRow,表示表格中的数据列,与数据栏组合成数据存储的单元。
    • IsNull():判断指定的字段是否为 NULL 值。
    • ItemArray:将 DataRow 中的数据转换成数组。
  • DataColumn,表示表格中的字段。
  • DataView,展示数据的辅助组件,类似于数据库中的查看表,并可设置过滤条件与排序条件。
    • Filter:设置 DataView 的过滤条件。
    • Sort:设置 DataView 的排序条件。
    • ToTable():将套用过滤与排序后的属性转换为 DataTable 对象。
  • DataRelation,可在 DataTable 之间设置字段间的关系。
  • Constraint,设置字段的条件约束,例如 ForeignKeyConstraint 为外部键限制,而 UniqueConstraint 则确保了字段中的值都是唯一的。

DataSet和DataTable除了数据库的处理以外,也经常被用来管理应用程序中的数据,并且由于它可以存储在 XML 中的特性,也让它可以用来存储需要保存的应用程序信息。

ADO.NET 数据提供者

在 .NET Framework中,ADO.NET默认提供了四种数据来源:

  • SQL Server:由 System.Data.SqlClient 提供支持,是微软官方建议访问SQL Server时建议使用的数据提供者。
  • OLE DB Data Source:由System.Data.OleDb提供支持,可适用于OLE DB Provider for ODBC 以外的 OLE DB 数据提供者。
  • Oracle:由System.Data.OracleClient提供支持,但用户的计算机必须安装 Oracle Client 8.1.7 或更新版本才行(.NET Framework 1.1 开始支持)。
  • ODBC:补OLE DB Provider for ODBC的支持,由System.Data.Odbc 提供支持(.NET Framework 1.1 开始支持)。

其他厂商亦为不同的数据库提供数据来源:

  • DataDirect Technologies发行100%列管提供源,支持主流企业数据库 (Oracle, Sybase, DB2, SQL Server, Progress RDBMS)
  • OpenLink Software给大量的客户指定数据库发行提供源,包括到其他数据访问机构的桥接提供源,并可以在窗口下微软自己或者Mono的CLR实做下支持。
  • MySQL为本身的 MySQL Database Server 提供了 ADO.NET 的本地数据提供者。
  • Oracle自行开发的 .NET Data Provider。

工厂方法

在.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的集成

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.InferXmlSchema("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 指令,但一开始它并不会自动产生,而是要靠调用方法来取得:

  • DbCommandBuilder.GetInsertCommand()
  • DbCommandBuilder.GetUpdateCommand()
  • DbCommandBuilder.GetDeleteCommand()

最常使用到的地方是和 DataAdapter 并用时,但它要求传入的 SELECT 语句所选择的列集合中必须要有主键或者唯一键[8],否则无法产生,同时自动产生的指令因为判断条件很多,对性能可能会有些影响。

Visual Studio的支持

ADO.NET和Visual Studio开发工具几乎已经是无缝的集成了,开发人员可以利用Visual Studio来创建强型别(strong-typed)的DataSet,到了Visual Studio 2005时更能够在Windows Forms应用程序中使用TableAdapter(Typed DataSet 和 DataAdapter 集成的产物)来开发应用程序(不会再看到 DataAdapter,但使用上差不多)[9]。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[10],这样的改变,让习惯使用 ADO 的 VB/ASP 开发人员会有某种程度的不习惯,同时让 ADO.NET 的学习会较 ADO 有较些许的复杂性,因此有部分新入门或是VB 6.0/ASP开发人员会在学习.NET Framework或是使用VB.NET开发应用程序时,在 .NET Framework 中使用 ADO 来连接数据来源。但在 .NET Framework 应用程序使用 ADO 的话,.NET Framework会因为要多一层COM和.NET数据之间的转换,会让应用程序性能有少部分的损耗[11]

ADO.NET 的进化

随着网络应用程序的进化,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完成时仍无法全部完成[12],因此微软将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 端点数据服务的数据供应层,即为 ADO.NET Data Services。

参考与注释

  1. ^ COM+, A Windows 2000 technology showcase
  2. ^ ADO+
  3. ^ 此概念的原型为 In-Memory Database,又称IMDB,可在存储器中运行的数据库,然而以当时的环境(2000年初,存储器尚未跌到目前的价格水平),以及技术不成熟的情况下,这项技术在 Windows 2000 RC2 时被抽离。
  4. ^ 有着和它相同遭遇的还有ASP+(改名为ASP.NET)。
  5. ^ ADO.NET 架构
  6. ^ 截取和修改 ADO.NET 中的数据
  7. ^ DataSet、DataTable 及 DataView (ADO.NET)
  8. ^ .NET Framework 开发人员指南 - 使用CommandBuilder生成命令(ADO.NET)
  9. ^ TableAdapter
  10. ^ ADO.NET ─ ADO 开发人员指引
  11. ^ Revisiting the Use of ADO in .NET Applications
  12. ^ A First Look at ObjectSpaces in Visual Studio 2005

相关条目







stock | retire | vm
Why are we here?
All text is available under the terms of the GNU Free Documentation License
This page is cache of Wikipedia. History