ADO.NET

✍ dations ◷ 2024-12-23 08:13:29 #微软API,计算机编程,数据管理,计算机程序库,.NET,数据库API

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

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)的项目,项目中包含了:

ADO+即为Storage+的一支。

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

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

ADO.NET由连线数据源(connected data source)以及离线数据模型(disconnected data model)两个部分构成,这两个部分是相辅相成的。

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

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


在.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.value = userName;//绑定用户名paras.value = password;//绑定用户密码
  • ConnectionStringBuilder:提供一种用于创建和管理由 Connection 对象使用的连接字符串的内容的简单方法。 所有 ConnectionStringBuilder 对象的基类均为 DbConnectionStringBuilder 类。
  • CommandBuilder :自动生成 DataAdapter 的命令属性或从存储过程中派生参数信息,并填充 Command 对象的 Parameters 集合。 所有 CommandBuilder 对象的基类均为 DbCommandBuilder 类。

离线数据模型

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

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);        }    }    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.Columns,                     custDS.Tables.Columns).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语句所选择的列集合中必须要有主键或者唯一键,否则无法产生,同时自动产生的指令因为判断条件很多,对性能可能会有些影响。

ADO.NET和Visual Studio开发工具几乎已经是无缝的集成了,开发人员可以利用Visual Studio来创建强类型(strong-typed)的DataSet,到了Visual Studio 2005时更能够在Windows Forms应用程序中使用TableAdapter(Typed DataSet和DataAdapter集成的产物)来开发应用程序(不会再看到DataAdapter,但使用上差不多)。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,这样的改变,让习惯使用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之间的数据转换,会让应用程序性能有少部分的损耗。

下述示例,用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完成时仍无法全部完成,因此微软将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。

相关