掌握ADO.NET的十个热门技巧(二)
2024-07-10 13:02:46
供稿:网友
7. adox可以帮你得到并改变schema信息
ado.net并没有为得到并管理schema信息提供一个完全的对象模式。你应该用activex data objects extensions for data definition language and security (adox)或用每个数据库提供的本地功能来得到并改变schema信息。adox是ado对象的一个扩展,它包括用来创建和修改schema的对象。你可以编写适用于各种数据源的代码(不管本地语法有什么不同),因为adox是管理schema的一个基于对象的方法。
你可以用一个data reader对象来读(不是设置)简单的schema信息。所有的data reader类(oledbdatareader、sqldatareader、oracledatareader)都提供了一个getschematable方法,该方法可以读取查询到的列的元数据信息。getschematable返回一个datatable对象(格式是每列一行)和固定的一组包含信息的列。返回的元数据可以分成三类:列元数据、数据库特征和列属性。返回的列可以是allowdbnull、isautoincrement、columnname、isexpression、isreadonly和numericprecision等。在msdn资料中有完整的列表(见附加资源)。
在调用executereader时,如果你执行keyinfo命令,那么getschematable方法就可以返回更精确的数据。你可以将keyinfo行为同缺省的行为结合起来,执行一个单独的命令并得到schema和数据:reader = cmd.executereader( _
commandbehavior.keyinfo or _
commandbehavior.closeconnection)
只有执行keyinfo,iskey、basetablename、isaliased、isexpression和ishidden字段的值才能被正确返回。如果执行keyinfo,关键的列(如果有)通常是添加在结果集的底部的,但不给它们返回数据。
8. 用一个派生的类和自定义的串行化来节省空间
只有两个ado.net对象是被标记为可串行化的——datatable和dataset。.net framework中的串行化是通过formatter对象来完成的,它们可以将一个对象实例保存到一个二进制或一个soap流(stream)中。.net formatter用reflection来提取任何必要的信息。然而,如果这个类实现了iserializable接口,那么.net formatter就会给接口的方法让步,让它们负责拷贝需要串行化到一个内存缓冲器中的所有的信息。datatable和dataset类都通过iserializable接口支持串行化。
如果你将一个datatable或一个dataset串行到一个二进制(binary stream)中,你应该可以得到非常紧凑的输出结果。虽然你得到的结果文件是最小的,但遗憾的是,它实际上并不小。荒谬的是,你保存到一个二进制的dataset比你用writexml方法保存到xml的同样的dataset要大很多。
要解释这种情况,我们需要来看看ado.net对象是用什么方式被串行起来的。在串行一个dataset对象时,它将基于xml的diffgram表示法保存在formatter的缓冲器中。在串行一个datatable时,它首先创建了一个临时的dataset对象,将它定义为它的parent,然后作为一个diffgram串行起来。
一个diffgram是一个xml流,它提供了一个dataset中表和行的有状态的表示法。一个diffgram文件是很详细的,有些冗长。diffgram包含当前的数据,以及被修改的行和未解决的错误的初始值。当我们保存一个dataset或一个datatable时,所有这些信息就会被传递给serializer。被串行化的对象总是包含xml数据,因此即使当输出流是二进制的时,最后的输出结果仍然会很大。
你可以创建一个继承datatable或dataset的新的可串行化的类来解决这个问题,并且更有效地保存ado.net对象。你必须用<serizlizable()>属性来标记新类,即使父类是可以串行化的。实际上,串行性(serizlizability)并不是一个可以自动继承的类属性。你从datatable或dataset构建的新类也可以实现iserializable接口。当然,你可以为新类选择一个不同的串行化方案。一个简单而有效的方法就是将datatable类的所有成员映射到数组和值成员中(见列表1)。
运用一个派生的类和一个自定义的串行化方案可以为一个dataset对象节省多达80%的磁盘空间。节省的空间的比率取决于dataset中的数据类型。你的数据越基于文本,节省的空间越多。然而,运用二进制的blob字段只可以节省大约25%的空间(下载一个完整的例子)。
9. 选择一个适合你的数据的分页机制
datagrid服务器控件使我们可以更容易地在web应用程序中以长度可变的页面来显示数据了。该控件有绑定和格式化功能,它可以接受一个ado.net数据对象并为浏览器生成html代码。出于性能的原因,在页面的视图状态,datagrid并没有缓存数据源的内容。因此,当返回页面时,你就必须填充grid。要实现这一点可以用两种方法:在web服务器上将数据源作为整体或一部分缓存起来,然后读回;或者对每个请求从物理数据库加载所需的记录。如果你选择第一种方法,那么数据就从存储中只被读取一次,保存在一个缓存中,并为以后的postback事件读回。我们通常用内存中的全局对象(如session或cache)来保存这个数据。我们用dataset来搜集所有需要的数据并将它保存在内存中。将一个dataset对象保存在session中同ado中的线程含义并不一样,但是通过减少web服务器可用的内存仍可以影响可扩展性。
如果要显示的数据是特定于session的,那么在每次返回页面时加载记录页面就比用一个dataset和asp.net全局对象来缓存数据要好。编写得很好的sql代码可以将结果集分成许多页,再加上datagrid控件内置的自定义分页机制,我们就可以得到最佳的解决方案来保持asp.net应用程序的可扩展性和良好的性能了。
对于windows应用程序,我的建议正好相反。台式应用程序很适合应用断开的编程模式(dataset和其它ado.net对象使这种模式变得更简单了)。当然,这并不意味着,你可以在客户端无忧无虑地下载成千上万的记录。尽管你可以将ado.net对象用于任何种类的.net应用程序,但如何使用它们是随具体情况的不同而不同的。
10. 访问多个结果集
根据查询的语法,你可以返回多个结果集。缺省情况下,data reader是位于第一个结果集上的。你可以用read方法在当前结果集中滚动查看记录。在找到最后一个记录时,read方法返回false,不再继续读取。你应该用nextresult方法转移到下一个结果集。如果没有更多的需要读的结果集了,那么该方法返回false。下面的代码说明了如何在所有返回的结果集中访问所有的记录:dim reader as sqldatareader
cmd.connection.open()
reader = cmd.executereader()
do
' move through the first resultset
while reader.read()
' access the row
end while
loop while reader.nextresult()
reader.close()
cmd.connection.close()
当你读一个行的内容时,可以通过索引或名称来识别列。运用索引可以更快,因为提供者可以直接进入到缓冲器中。如果你指定列名,提供者就用getordinal方法将名称转换成相应的索引,然后执行基于索引的访问。注意,对于sql server data reader来说,所有的getxxx方法实际上都调用了相应的getsqlxxx方法。对于oracle data reader来说,情况是类似的,本地数据总是被写进.net framework类型中。oracledatareader类为它自己的内部类型提供了一组私有的getxxx方法。这些方法包括getoraclebfile、getoraclebinary和getoracledatetime等。相反,ole db和odbc readers只有单独的一组get方法。
.net framework 1.1版通过添加方法hasrows扩展了data readers的编程接口,该方法返回一个boolean值来说明是否有很多行需要读。(这是asp.net 1.0的一个不足之处。)然而,该方法并没有告诉我们有效的行的数量。同样,也没有方法或技巧使我们提前知道已经返回了多少结果集。
在oracle数据库编程中,一个查询或一个存储过程返回的多个结果集是通过多个ref cursor对象处理的。有多少结果集,你就必须将多少输出参数同命令关联起来,以便nextresult方法可以用于oracle数据库。在命令文本中,一个ado.net结果集同一个oracle ref cursor是一致的。输出参数名必须与指针名匹配,它们的类型必须是oracletype.cursor。例如,如果要运行的存储过程(或命令文本)引用了两个指针(employees和orders),那么下面的代码就说明了如何进行设置以返回两个结果集:dim p1 as oracleparameter
p1 = cmd.parameters.add("employees", oracletype.cursor)
p1.direction = parameterdirection.output
dim p2 as oracleparameter
p2 = cmd.parameters.add("orders", oracletype.cursor)
p2.direction = parameterdirection.output
在上面的代码中,cmd是一个oraclecommand对象,它指向一个命令或一个存储过程。它执行代码,创建了两个ref cursor,称为employees和orders。ref cursor的名称和ado.net输出参数的名称必须匹配。
ado.net对象模式包含两个主要的部分——托管提供者和database-agnostic的容器类,如dataset。托管提供者是数据源连接器的新类型;它们代替了基于com的ole db提供者。到我写这篇文章时为止,只有少数几个托管提供者来连接商业dbms。.net framework 1.1版只包含几个本地提供者——用于sql server、oracle和所有ole db的提供者和odbc驱动程序。第三方的供应商也支持mysql并为oracle提供了可供选择的提供者。
ado.net看起来类似于ado,而且托管提供者在结构上同ole db提供者也是可以相比的。除了这些相似点外,在ado.net中进行有效的编程还需要一套新的技巧和好的方法。在大多数情况下,你可以通过编写代码得到很多技巧,并积累对象模式方面的经验。当你在进一步研究ado.net编程时,记住我在本文中所讲的这10个ado.net技巧吧。
关于作者:
dino esposito是wintellect(www.wintellect.com)的培训讲师和顾问,他主要负责ado.net和xml课程。他是building web solutions with asp.net and ado.net和applied xml programming for microsoft.net(microsoft press)的作者,也是www.vb2themax.com的协办人。他的联系方式是[email protected]。