电信主站 网通分站
购买流程 付款方式 常见问题 在线提问 续租服务 购物车
用户名: 密 码: 忘记密码?
首 页
域名注册
虚拟主机
双线主机
服务器租用
VPS主机
企业邮局
代理专区
客服中心
虚拟主机行业资讯 虚拟主机评测对比 互联网最新动态 技术学院 站长资讯 在线教程 网站运营
搜索优化 服务器 网络编程 图形图象 站长之家 网页制作 操作系统
冲浪宝典 软件教学 视频通信 办公软件 邮件系统 网络安全 认证考试
您当前位置:西部数码->资讯中心-> 在线教程-> 数据库
ADO.NET的开发场景及传统ADO的处理-.NET教程,数据库应用
作者:网友供稿 点击:52
  西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!虚拟主机可在线rar解压,自动数据恢复设置虚拟目录等.虚拟主机免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金!
文章页数:[1] 
出处: msdn开发精选
当转为使用ado.net时,您将需要了解如何应对以前知道用ado处理而现在必须用ado.net解决的场景。就像使用visual basic、c++和asp开发的n层解决方案经常要依赖ado来满足数据访问需要一样,windows?窗体、web窗体和web服务也要依赖ado.net。我曾经从使用传统ado开发的角度讨论了如何使用ado.net来处理一些数据访问的场景。其中的一些主题包括将行集保留为xml、处理只进流水游标和执行command对象的多种方式。在文中,我将继续讨论使用ado.net的开发场景,以及使用传统的ado技术是如何处理的。我将从经常使用的传统ado的只进、静态、键集和动态游标的情况开始讨论。我还要讨论如何处理并发性问题,以及断开连接的行集如何从ado演变到ado.net。然后,我会说明如何将使用传统的ado处理批量更新的代码转换为使用ado.net(用dataadapter及其四个命令对象)的代码。

  分散recordset的功能

  在ado.net中,ado recordset的大多数功能都分到了三个主要对象中:datareader、dataset和dataadapter(参见图1)。


图1 ado recordset

  ado.net datareader对象被设计成服务器端的只进、只读游标。ado.net dataset对象是行集断开连接的存储工具。它存储记录,但不持有与数据源的连接,事实上,它并不关心其行集从哪个数据源派生。dataset在内存中存储时是一个二进制对象,但是它可以轻松地从xml序列化和序列化为xml。这与ado recordset对象从其相关联的connection对象断开连接(通过recordset的activeconnection属性)时将cursortype设置为adopenstatic、将cursorlocation设置为aduseclient是类似的。ado.net dataadapter对象是连接与dataset对象之间的桥梁,它可以通过一个连接从数据源加载一个dataset,并用dataset中存储的已更改的内容更新数据源。ado recordset的行为取决于其属性的设置(包括cursortype和cursorlocation属性)。在ado.net中构建了不同的对象来处理这些特定的情况,而无需用一个对象来应对所有情况。

  流水游标

  传统的ado公开了四个不同类型的游标,可以改变ado recordset对象的运作方式。ado recordset对象的行为方式因其cursortype属性的设置情况互不相同,可能有非常大的差异。例如,通过将cursortype设置为adopenforwardonly,recordset可保持与其数据源的连接,而且必须按只进方向进行遍历。然而,当您将cursortype属性设置为adopendynamic时,recordset可向前或者向后遍历,甚至可以使游标跳到某个特定行。通过其cursortype和cursorlocation属性,ado recordset对象采用了一种将许多解决方案包装到单个对象中的方式。而ado.net采用的方法则不相同,它设计由不同的对象和方法来处理各种特定情况。在传统的ado中,只进、只读游标是通过将cursortype设置为adopenforwardonly、将cursorlocation设置为aduseserver(这也是默认设置)实现的,这将使recordset对象采用只进的服务器端游标形式。movenext方法将recordset重定位到下一行,而moveprevious方法则根本不允许使用,但是可以调用recordset的movefirst方法。这有些容易引起误解,因为该方法并不将recordset重定位到当前行集的开始;相反,它调用原来的sql语句,从头开始重新填充recordset,因此会再次移动到第一个记录。每次执行传统ado recordset的movefirst方法时(cursortype设置为adopenforwardonly),通过打开sql事件探查器工具,观察sql的执行情况,可以很容易地看到这一点。在需要逐个遍历成千上万(乃至更多)行中的每行或者在需要小一些的行集但是只需遍历一次(可能为了加载到一个选取列表)时,这种游标是非常常用的:

-- forward-only firehose cursor in asp and ado
set ors.activeconnection = ocn
ors.locktype = adlockreadonly
ors.cursortype = adopenforwardonly
ors.cursorlocation = aduseserver
ors.open ssql

  最接近这种传统的ado流水游标的等效物是ado.net datareader对象。和传统的只进ado recordset一样,datareader在打开的同时保持着与其数据源的连接,而且只能以前进的方向进行遍历。但是,它们还是有区别的。区别之一是,专门为某个data provider(如sql server?(sqldatareader类)或者odbc数据源(odbcdatareader类))编写单独的datareader类型。ado.net datareader对象非常高效,这是因为它是专门为实现只进、只读游标这一目的而构建的。传统ado的只进游标是通过相同的recordset对象(作为一个断开连接的recordset甚至是一个数据源敏感的recordset)实现的。而datareader只是为了作为轻型流水游标而设计的。

//-- asp.net and ado.net in c#
sqldatareader odr = ocmd.executereader();

  数据敏感游标

  传统ado的只进游标现在由datareader对象处理。但是,对基础数据库中的更改非常敏感的键集和动态服务器端游标(cursortype = adopenkeyset和cursortype = adopendynamic)又怎么样呢?当前版本的ado.net并没有公开多方向、可滚动、可更新的服务器端游标。但是,ado.net确实提供了许多方式避免使用这些类型的服务器端游标,因为有其他推荐的技术可以考虑。除了使用dataset结合dataadapter来检索和更新数据以外,还有一些好的替代方案在客户端使用可滚动的服务器端游标。例如,可以使用存储过程,在运行服务器端sql处理时这是非常高效的。还可以通过服务器端只进游标用datareader检索数据,然后用command对象分别进行更新。但是,经常有比使用可更新的服务器端游标更高效的修改数据的方式。

  在一个n层环境中,服务器端可滚动游标的问题之一在于,它们需要在服务器上保持状态。因此,如果应用程序在中间层使用服务器端可滚动游标,客户端层就需要维持与可滚动游标所在的业务层的连接。这样,在同样使用可滚动游标的客户端应用程序屏幕存在期间,业务对象都需要保留。各种文档中已经很好地记录了这些可伸缩性问题,但是,在一些情况下服务器端游标还是很有价值的,如需要只进流水游标的情况。例如,在应用程序需要注重数据并发性问题的时候就要用到服务器端游标。使用传统的ado,recordset可以用一个动态游标打开,这种游标使recordset能够获悉所有其他用户进行的插入、更改和删除操作。这种服务器端游标对于其他用户所做的更改非常敏感,可以在应用程序出现并发性问题的时候用于采取主动措施。例如,传统ado中的动态游标经常用于这样的场合:应用程序准备在数据库中保存更改,但是又需要确保首先知道是否有其他人更改了同一条记录。当用户更改动态recordset中一个记录值的时候,该值将自动在服务器端动态recordset中更新:

-- dynamic cursor in asp and ado
set ors.activeconnection = ocn
ors.locktype = adlockoptimistic
ors.cursortype = adopendynamic
ors.cursorlocation = aduseserver
ors.open ssql

服务器端游标与并发性

  并发性问题在许多企业级应用程序中是合理而且常见的问题,但是使用服务器端游标以解决这一问题的代价却可能非常之高。那么在ado.net中有什么替代方式处理并发性问题呢?常见的技术是允许应用程序对数据库提交更改,然后在遇到并发性问题时引发特殊类型的异常。有一个特殊类型的异常名为dbconcurrencyexception,它是从exception基类派生而来的。因为dataset将每列的原始值和拟更改值存储到一行中,它知道如何将值与数据库进行比较以自动寻找并发性问题。例如,假定用户将firstname字段的值由lloyd更改为lamar,并将更改提交给数据库,存储在dataset中的更改将通过dataadapter的update方法传递给数据库。在数据真正保存之前,如果数据库中的名字不再是lloyd,现在变成了lorenzo,将会引发一个dbconcurrencyexception异常。此异常可以以最适合应用程序需要的任何方式进行捕获和处理,例如,可以采取“最后者优先”的规则,使用户可以选择取消更改、改写或者强制更新或其他技术。

public dataset savedata(dataset ods)
{
 string smethodname = "[public void savedata(dataset ods)]";

 //==========================================================
 //-- establish local variables
 //==========================================================
 string sprocname;
 string sconnstring = "server=(local);database=northwind;integrated
 security=sspi";
 sqldataadapter oda = new sqldataadapter();
 sqltransaction otrn = null;
 sqlconnection ocn = null;
 sqlcommand oinscmd = null;
 sqlcommand oupdcmd = null;
 sqlcommand odelcmd = null;

 try
 {
  //==========================================================
  //-- set up the connection
  //==========================================================
  ocn = new sqlconnection(sconnstring);

  //==========================================================
  //-- open the connection and create the transaction
  //==========================================================
  ocn.open();
  otrn = ocn.begintransaction();

  //==========================================================
  //-- set up the insert command
  //==========================================================
  sprocname = "prinsert_order";
  oinscmd = new sqlcommand(sprocname, ocn, otrn);
  oinscmd.commandtype = commandtype.storedprocedure;
  oinscmd.parameters.add(new sqlparameter("@scustomerid", sqldbtype.nchar, 5, "customerid"));
  oinscmd.parameters.add(new sqlparameter("@dtorderdate",
  sqldbtype.datetime, 8,"orderdate"));
  oinscmd.parameters.add(new sqlparameter("@sshipcity",
  sqldbtype.nvarchar, 30, "shipcity"));
  oinscmd.parameters.add(new sqlparameter("@sshipcountry", sqldbtype.nvarchar, 30, "shipcountry"));
  oda.insertcommand = oinscmd;

  //==========================================================
  //-- set up the update command
  //==========================================================
  sprocname = "prupdate_order";
  oupdcmd = new sqlcommand(sprocname, ocn, otrn);
  oupdcmd.commandtype = commandtype.storedprocedure;
  oupdcmd.parameters.add(new sqlparameter("@norderid", sqldbtype.int, 4, "orderid"));
  oupdcmd.parameters.add(new sqlparameter("@dtorderdate", sqldbtype.datetime, 8,"orderdate"));
  oupdcmd.parameters.add(new sqlparameter("@sshipcity", sqldbtype.nvarchar, 30, "shipcity"));
  oupdcmd.parameters.add(new sqlparameter("@sshipcountry", sqldbtype.nvarchar, 30, "shipcountry"));
  oda.updatecommand = oupdcmd;

  //==========================================================
  //-- set up the delete command
  //==========================================================
  sprocname = "prdelete_order";
  odelcmd = new sqlcommand(sprocname, ocn, otrn);
  odelcmd.commandtype = commandtype.storedprocedure;
  odelcmd.parameters.add(new sqlparameter("@norderid", sqldbtype.int, 4, "orderid"));
  oda.deletecommand = odelcmd;

  //==========================================================
  //-- save all changes to the database
  //==========================================================
  oda.update(ods.tables["orders"]);

  otrn.commit();
  ocn.close();
 }
 catch (dbconcurrencyexception exdbconcurrency)
 {
  //=======================================================
  //-- roll back the transaction
  //=======================================================
  otrn.rollback();

  //--------------------------------------------------------
  //-- may want to rethrow the exception at this point.
  //-- this depends on how you want to handle the concurrency
  //-- issue.
  //--------------------------------------------------------
  //-- throw(exdbconcurrency);
 }
 catch (exception ex)
 {
  //==========================================================
  //-- roll back the transaction
  //==========================================================
  otrn.rollback();

  //--------------------------------------------------------
  //-- rethrow the exception
  //--------------------------------------------------------
  throw;
 }
 finally
 {
  oinscmd.dispose();
  oupdcmd.dispose();
  odelcmd.dispose();
  oda.dispose();
  otrn.dispose();
  ocn.dispose();
 }
 ocn.close();
 return ods;
}
             图2 捕获ado.net中的并发性问题

  在图2 的代码中,可以注意到一个示例方法,它接收一个dataset参数,并将dataset中的更改通过dataadapter的update方法提交给数据库。如果检测到并发性问题,将由一个特定的catch代码块引发和捕获dbconcurrencyexception异常。此时,事务可以回滚,然后还可重新引发异常,直到最终异常被客户端应用程序捕获,从而得以通知用户并询问用户希望如何处理。这里同样要仔细地考虑catch代码块的顺序。例如,如果首先出现一般性的catch代码块,那么它已经捕获了并发性问题。异常处理的关键在于,要确保针对更特定异常的catch代码块在其他catch代码块之前出现,从而可由特定catch代码块首先捕获特定异常。
批量更新

  ado recordset对象经常会用到的一种情形是进行批量更新。例如,在一个n层应用程序中,ado recordset可检索一个行集,将其从数据源中断开连接,并将recordset发送到客户端层。在客户端应用程序中,对几行的更改将在断开连接的ado recordset中进行。然后recordset可被发回给中间层,通过ado connection对象与数据库重新连接,其更改可通过updatebatch方法应用于数据库。updatebatch方法将接受recordset中的所有被更改的行,并将更改应用于recordset源查询中指示的源表。ado.net也有内置的处理批量更新的功能。dataset是一个自成一体的、总是断开连接的行集集合,它可以存储行集中行与列的原始值和当前值。dataset可以被发送给客户端应用程序,由后者进行更改操作。dataset然后被发送到中间层,其更改将通过dataadapter对象应用于数据库。图2说明了ado.net dataadapter如何使用其不同的command对象执行select、insert、update和delete sql命令:

//-- setting the ado.net dataadapters command objects
oda.insertcommand = oinscmd;
oda.updatecommand = oupdcmd;
oda.deletecommand = odelcmd;
oda.update(ods.tables["orders"]);

  ado.net技术在对数据库应用更新的方式上比传统的ado批量更新技术更灵活。传统ado是通过查看ado recordset的source属性来确定保存更改的位置。例如,假定recordset的源是:

select orderid, customerid, orderdate, shipcity, shipcountry from orders

  这种情况下,在对recordset已经进行批量更改而且调用了recordset的updatebatch方法之后,recordset必须指向发送更改的位置。它查看源的sql语句,并确定orders表的主键(orderid)在语句中而且只用到了一个表。因此,它可以使用sql语句创建update、insert和delete语句对orders表进行处理。为了能够派生操作查询,recordset必须知道唯一行标识符。同样,存储过程不能用于这种技术更新数据库,因为语句是隐式创建的。以下ado 2.x代码说明了包含orders的recordset如何在客户端应用程序中进行更新:

-- using traditional ado,
-- updating two rows in the client tier
ors.find "customerid = vinet"
ors.fields("shipcity") = "somewhere"
ors.update
ors.find "customerid = chops"
ors.fields("shipcity") = "elsewhere"
ors.update

  之后,一旦recordset传回给中间层,将调用以下代码从而将已完成的更改应用于数据库:

-- using traditional ado,
-- sending both updated rows to the database, in the business tier
ors.updatebatch

  ado.net commandbuilder的工作方式与此类似,因为它可以自动地为dataadapter生成updatecommand、insertcommand和deletecommand对象。自动生成命令的过程也会带来系统开销。显式地指定要使用的insert、update和delete语句效率更高。与显式指定命令相比,使用ado.net commandbuilder意味着性能更低下,在对数据源应用更改方式上的控制也更差。准确的select、insert、update和delete语句通常在设计时是已知的,可以用来生成ado.net dataadapter的四个不同command属性,它们甚至可以用存储过程的名称表示。ado.net的这一功能是传统的ado所缺乏的一个关键部分,这可以大大提高传统ado的批量更新技术的灵活性。当然,这需要在设计时进行更多的编码工作,但为了获得它所提供的更大灵活性(可使您非常具体地指定sql语句或更常见地用于为每个操作指定存储过程),付出这些努力也是值得的。

  断开连接的行集

  通过适当设置一些属性,ado recordset对象可以从其数据源中断开连接。通过断开连接,它能够在内存中存储整个行集,并在使用传统ado的应用程序之间传递。以下代码示例将断开recordset的连接:

-- disconnecting an ado recordset
ors.cursorlocation = aduseclient
ors.cursortype = adopenstatic
ors.locktype = adlockbatchoptimistic
ors.open
set ors.activeconnection = nothing

  断开recordset的连接涉及的关键属性是cursortype和cursorlocation。cursorlocation必须设置为aduseclient,表示行集应该存储在recordset的内存中。cursortype应该设置为adopenstatic,这将允许行集的游标能够在任何方向上移动,但是不允许行集自动对基础数据库的更改处于敏感状态。通过结合使用这两个设置,recordset可以断开连接,但是,全部步骤还没有完。一旦打开recordset,因为它真的断开连接了,那么它的activeconnection属性应该被设置为关键字nothing,就像上一个代码示例中看到的那样。构建ado recordset是为了在断开连接和已连接这两种模式下工作。但是在ado.net中,当dataset完全断开连接时,datareader要维持连接。dataset是断开连接的ado recordset的继承者,因为它实现了一个客户端游标,可以向任何方向滚动,而且ado.net dataset还支持许多其他功能。传统的ado recordset对象提供的断开连接功能非常有限,而ado.net dataset对象是专门为断开连接而设计的。与recordset不同,dataset可以在datatable对象内存储和表示多个行集,甚至还可以使用datarelation对象将它们互相联系起来。下面是使用sqldataadapter的fill方法创建和填充dataset的一种常见方式:

//-- creating and filling an ado.net dataset
dataset ods = new dataset("mydataset");
oda.fill(ods);

  dataset可以施加关系、主键约束和外键约束,甚至实现列表达式。ado recordset还能够使用它的数据构形功能返回层次化的结果。虽然有这些层次化功能,但是它们需要使用我个人认为非常笨拙的数据构形语法。就此方面而言,ado.net dataset更加高效也更加直观,因为它就是为使用其datarelation和datatable对象处理关系数据结构而构建的。

  在之前,我讨论了几个传统的ado 2.x开发中常用的技术,以及如何将它们转换到ado.net。ado.net是ado 2.x发展的下一阶段,许多传统ado中的功能都将在ado.net中进行全面改造。例如,ado 2.x中添加了open和save方法的功能以支持xml,而xml支持从一开始就已经被纳入到ado.net的设计蓝图中了。ado的一些功能发生了很大变化,变化之一就是recordset的许多游标类型被分为多个不同的各有侧重的对象,例如datareader、dataset和dataadapter。其他功能(例如connection和command类)的改变则不是那么明显。但是,说到底,从ado到ado.net的迁移还是相对比较易懂的,这是因为ado.net的设计构筑于传统ado的使用体验之上。
共3页。

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!
相关主题
文章页数:[1] 
Google
热门文章
·数据库开发个人总结(ADO.NET小结)-.NET教程,数据库应用
·怎么由DataSet将数据导入Excel?-.NET教程,数据库应用
·动态创建SQL Server数据库、表、存储过程-ASP教程,数据库相关
·Win32环境下动态链接库(DLL)编程原理-.NET教程,数据库应用
·封装的ADO.NET对数据库操作经典类-.NET教程,数据库应用
·在DataGridView中获得DataGridViewCheckBoxColumn的状态-ASP教程,数据库相关
·DataGrid使用心得(附大量代码)-ASP教程,数据库相关
·用代码创建DataGrid的多链接及checkbox事件响应-.NET教程,数据库应用
·ADO.NET 的最佳实践技巧-.NET教程,数据库应用
·转载: 用纯ASP代码实现图片上传并存入数据库中

最新文章
·根据数据表中数据,生成Powerpoint幻灯片-ASP教程,数据库相关
·DataGrid中的按钮反选事件与NamingContainer(命名容器)-downmoon-ASP教程,数据库相关
·使用用VB处理MYSQL数据库中二进制数据问题-.NET教程,VB.Net语言
·关于DataGridView中如何接收处于编辑状态下的当前信息-ASP教程,数据库相关
·在DataGridView中获得DataGridViewCheckBoxColumn的状态-ASP教程,数据库相关
·.net下访问Access数据库需要注意的问题-.NET教程,Asp.Net开发
·ActiveMQ4.1+Spring2.0的POJO JMS方案(上)-.NET教程,数据库应用
·ASP.NET 2.0中直接将Access数据库导入到Excel文件中-.NET教程,Asp.Net开发
·NET(C#)连接各类数据库-集锦-.NET教程,C#语言
·ASP.NET2.0连接SQL Server数据库详解-.NET教程,Asp.Net开发


 
 


版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!

特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
  打印  刷新  关闭
返回首页 |关于我们 | 联系我们 | 付款方式 | 创业联盟 | 虚拟主机 | 资讯中心 | 友情链接 | 网站地图

版权所有 西部数码(www.west263.com)
CopyRight (c) 2002~2006 west263.com all right reserved.
公司地址:四川成都市万和路90号天象大厦4楼 邮编:610031
电话总机:028-86262244 86263048 86263408 86263960 86264018 86267838
售前咨询:总机转201 202 203 204 206 208
售后服务:总机转211 212 213 214
财务咨询:总机转224 223 传真:028-86264041 财务QQ:点击发送消息给对方635483282
售前咨询QQ:点击发送消息给对方2182518 点击发送消息给对方241975952 点击发送消息给对方275026793 点击发送消息给对方408235859
售后服务QQ:点击发送消息给对方17708515 点击发送消息给对方307742704 点击发送消息给对方287976517 点击发送消息给对方363783715
《中华人民共和国增值电信业务经营许可证》编号:川B2-20030065号