Username: Password:

ADO.NET实用经验-.NET教程,数据库应用
来源:作者: 发布时间:2007-12-25 13:34:43

ado.net实用经验
ado.net作为微软最新的数据访问技术,已在企业研发中得到了广泛的应用。对于一线的研发人员来说,掌控基本的概念和技术之后,提高应用水平和解决实际问题的最有效手段,莫过于相互交流彼此的最好时间经验经验。在这篇文章中,两位ado.net专家向读者毫无保留地、详尽地介绍了很多实用经验。

  简介

  本文为您提供了在microsoft ado.net应用程式中实现和获得最好性能、可伸缩性连同功能的最好解决方案;同时也讲述了使用ado.net中可用对象的最好实践;并提出一些有助于优化ado.net应用程式设计的建议。

  .net框架数据提供程式

  .net框架中的数据提供程式(data provider)在应用程式和数据源之间起到桥梁作用。.net框架数据提供程式能够从数据源中返回查询结果、对数据源执行命令、将dataset中的更改传播给数据源。本文包括有关哪个.net框架数据提供程式是最适合您需要的一些技巧。

  使用哪个.net框架数据提供程式?

  为了使您的应用程式获得最好性能,请使用最适合您的数据源的.net框架数据提供程式。有许多数据提供程式可供您的应用程式选用。

  连接到sql server 7.0或更高版本

  为了在连接到microsoft sql server 7.0或更高版本时获得最好性能,请使用sql server .net数据提供程式。sql server .net数据提供程式的设计目的就在于不通过任何附加技术层就能够直接访问sql server。

  连接到odbc数据源

  odbc .net数据提供程式可在microsoft.data.odbc命名空间中找到,他的体系结构和用于sql server和ole db的.net数据提供程式相同。odbc .net数据提供程式遵循命名约定-以“odbc”为前缀(例如,odbcconnection),并使用标准odbc连接字符串。

  使用datareader、dataset、dataadapter和dataview

  ado.net提供以下两个对象,用于检索关系数据并将其存储在内存中:dataset和datareader。dataset提供一个内存中数据的关系表示形式,一整套包括一些表在内的数据(这些表包含数据、对数据进行排序并约束数据),连同表之间的关系。datareader提供一个来自数据库的快速、仅向前、只读数据流。

  当使用dataset时,经常会利用dataadapter(也可能是commandbuilder)和数据源进行交互。当使用dataset时,也能够利用dataview对dataset中的数据应用排序和筛选。也能够从dataset继承,创建强类型dataset,用于将表、行和列作为强类型对象属性公开。

  下列主题包括的信息涉及:使用dataset或datareader的最好时机、怎样优化访问他们所包含数据、连同怎样优化使用dataadapter(包括commandbuilder)和dataview的技巧。

  dataset和datareader

  当设计应用程式时,要考虑应用程式所需功能的等级,以确定使用dataset或是datareader。

  要通过应用程式执行以下操作,就要使用dataset:

  1) 在结果的多个离散表之间进行导航。

  2) 操作来自多个数据源(例如,来自多个数据库、一个xml文档和一个电子表格的混合数据)的数据。

  3) 在各层之间交换数据或使用xml web服务。和datareader不同的是,dataset能传递给远程客户端。

  4) 重用同样的记录集合,以便通过缓存获得性能改善(例如排序、搜索或筛选数据)。

  5) 每条记录都需要执行大量处理。对使用datareader返回的每一行进行扩展处理会延长服务于datareader的连接的必要时间,这影响了性能。

  6) 使用xml操作对数据进行操作,例如可扩展样式表语言转换(xslt转换)或xpath查询。

  对于下列情况,要在应用程式中使用datareader:

  1) 无需缓存数据。

  2) 要处理的结果集太大,内存中放不下。

  3) 一旦需要以仅向前、只读方式快速访问数据。

  注填充dataset时,dataadapter使用datareader。因此,使用dataadapter取代dataset提升的性能表现为节省了dataset占用内存和填充dataset需要的循环。一般来说,此性能提升只是象征性的,因此,设计决策应以所需功能为基础。

  使用强类型dataset的好处

  dataset的另一个好处是可被继承以创建一个强类型dataset。强类型dataset的好处包括设计时类型检查,连同microsoft visual studio.net用于强类型dataset语句结束所带来的好处。修改了dataset的架构或关系结构后,就能够创建一个强类型dataset,将行和列作为对象的属性公开,而不是作为集合中的项公开。例如,不公开客户表中行的姓名列,而公开customer对象的name属性。类型化dataset从dataset类派生,因此不会牺牲dataset的任何功能。也就是说,类型化dataset仍能远程访问,并作为数据绑定控件(例如datagrid)的数据源提供。假如架构事先不可知,仍能受益于通用dataset的功能,但却不能受益于强类型dataset的附加功能。

  处理强类型dataset中的空引用

  使用强类型dataset时,能够使用dataset的xml架构定义语言(xsd)架构来确保强类型dataset能够正确处理空引用。nullvalue标识符使您可用一个指定的值string.empty代替dbnull、保留空引用或引发异常。选择哪个选项取决于应用程式的上下文。默认情况下,假如碰到空引用,就会引发异常。

  刷新dataset中的数据

  假如想用服务器上的更新值刷新dataset中的值,就使用dataadapter.fill。假如有在datatable上定义的主键,dataadapter.fill会根据主键进行新行匹配,并且当更改到现有行时应用服务器上的值。即使刷新之前修改了这些数据,刷新行的rowstate仍被配置为unchanged。注意,假如没有为datatable定义主键,dataadapter.fill就用可能重复的主键值添加新行。

  假如想用来自服务器的当前值刷新表,并同时保留对表中的行所做的任何更改,必须首先用dataadapter.fill填充表,并填充一个新的datatable,然后用preservechanges值true将datatable合并到dataset之中。

  在dataset中搜索数据

  在dataset中查询和特定条件相匹配的行时,能够利用基于索引的查找提高搜索性能。当将primarykey值赋给datatable时,会创建一个索引。当给datatable创建dataview时,也会创建一个索引。下面是一些利用基于索引进行查找的技巧。

  1) 假如对组成datatable的primarykey的列进行查询,要使用datatable.rows.find而不是datatable.select。

  2) 对于涉及到非主键列的查询,能够使用dataview为数据的多个查询提高性能。当将排序顺序应用到dataview时,就会建立一个搜索时使用的索引。dataview公开find和findrows方法,以便查询基础datatable中的数据。

  3) 假如无需表的排序视图,仍能够通过为datatable创建dataview来利用基于索引的查找。注意,只有对数据执行多个查询操作时,这样才会带来好处。假如只执行单一查询,创建索引所需要的处理就会降低使用索引所带来的性能提升。

  dataview构造

  假如创建了dataview,并且修改了sort、rowfilter或rowstatefilter属性,dataview就会为基础datatable中的数据建立索引。创建dataview对象时,要使用dataview构造函数,他用sort、rowfilter和rowstatefilter值作为构造函数参数(和基础datatable一起)。结果是创建了一次索引。创建一个“空”dataview并随后配置sort、rowfilter或rowstatefilter属性,会导致索引至少创建两次。

  分页

  ado.net能够显式控制从数据源中返回什么样的数据,连同在dataset中本地缓存多少数据。对查询结果的分页没有唯一的答案,但下面有一些设计应用程式时应该考虑的技巧。

  1) 避免使用带有startrecord和maxrecords值的dataadapter.fill重载。当以这种方式填充dataset时,只有maxrecords参数(从startrecord参数标识的记录开始)指定的记录数量用于填充dataset,但无论怎样总是返回完整的查询。这就会引起不必要的处理,用于读取“无需的”记录;而且为了返回附加记录,会耗尽不必要的服务器资源。

  2) 用于每次只返回一页记录的技术是创建sql语句,将where子句连同order by子句和top谓词组合起来。此技术取决于存在一种可唯一标识每一行的办法。当浏览下一页记录时,修改where子句使之包含任何唯一标识符大于当前页最后一个唯一标识符的记录。当浏览上一页记录时,修改where子句使之返回任何唯一标识符小于当前页第一个唯一标识符的记录。两种查询都只返回记录的top页。当浏览上一页时,需要以降序为结果排序。这将有效地返回查询的最后一页(假如需要,显示之前也许要重新排序结果)。

  3) 另一项每次只返回一页记录的技术是创建sql语句,将top谓词和嵌入式select语句的使用结合在一起。此技术并不依赖于存在一种可唯一标识每一行的办法。使用这项技术的第一步是将所需页的数量和页大小相乘。然后将结果传递给sql query的top谓词,该查询以升序排列。再将此查询嵌入到另一个查询中,后者从降序排列的嵌入式查询结果中选择top页大小。实质上,返回的是嵌入式查询的最后一页。例如,要返回查询结果的第三页(页大小是10),应该书写如下所示的命令:

select top 10 * from
(select top 30 * from customers order by id asc) as table1
order by id desc


  注意:从查询中返回的结果页以降序显示。假如需要,应该重新排序。

  1) 假如数据不经常变动,能够在dataset中本地维护一个记录缓存,以此提高性能。例如,能够在本地dataset中存储10页有用的数据,并且只有当用户浏览超出缓存第一页和最后一页时,才从数据源中查询新数据。

  用架构填充dataset

  当用数据填充dataset时,dataadapter.fill方法使用dataset的现有架构,并使用从selectcommand返回的数据填充他。假如在dataset中没有表名和要被填充的表名相匹配,fill方法就会创建一个表。默认情况下,fill仅定义列和列类型。

  通过配置dataadapter的missingschemaaction属性,能够重写fill的默认行为。例如,要让fill创建一个表架构,并且还包括主键信息、唯一约束、列属性、是否允许为空、最大列长度、只读列和自动增量的列,就要将dataadapter.missingschemaaction指定为missingschemaaction.addwithkey。或,在调用dataadapter.fill前,能够调用dataadapter.fillschema来确保当填充dataset时架构已到位。

  对fillschema的调用会产生一个到服务器的额外行程,用于检索附加架构信息。为了获得最好性能,需要在调用fill之前指定dataset的架构,或配置dataadapter的missingschemaaction。

  使用commandbuilder的最好实践

  假设selectcommand执行单一表select,commandbuilder就会以dataadapter的selectcommand属性为基础自动生成dataadapter的insertcommand、updatecommand、和deletecommand属性。下面是为获得最好性能而使用commandbuilder的一些技巧。

  1) commandbuilder的使用应该限制在设计时或即席方案中。生成dataadapter命令属性所必需的处理会影响性能。假如预先知道insert/update/delete语句的内容,就显式配置他们。一个比较好的设计技巧是,为insert/update/delete命令创建存储过程并显式配置dataadapter命令属性以使用他们。

  2) commandbuilder使用dataadapter的selectcommand属性确定其他命令属性的值。假如dataadapter的selectcommand本身曾更改过,确保调用refreshschema以更新命令属性。

  3) 假如dataadapter命令属性为空(命令属性默认情况下为空),commandbuilder仅仅为他生成一条命令。假如显式配置了命令属性,commandbuilder不会重写他。假如希望commandbuilder为以前已配置过的命令属性生成命令,就将命令属性配置为空。

  批处理sql语句

  很多数据库支持将多条命令合并或批处理成一条单一命令执行。例如,sql server使您能够用分号“;”分隔命令。将多条命令合并成单一命令,能减少到服务器的行程数,并提高应用程式的性能。例如,能够将任何预定的删除在应用程式中本地存储起来,然后再发出一条批处理命令调用,从数据源删除他们。

  虽然这样做确实能提高性能,但是,当对dataset中的数据更新进行管理时,可能会增加应用程式的复杂性。要保持简单,可能要在dataset中为每个datatable创建一个dataadapter。

  用多个表填充dataset

  假如使用批处理sql语句检索多个表并填充dataset,第一个表用指定给fill方法的表名命名。后面的表用指定给fill方法的表名加上一个从1开始并且增量为1的数字命名。例如,假如运行下面的代码:

visual basic
dim da as sqldataadapter = new sqldataadapter("select * from customers; select * from orders;", myconnection)
dim ds as dataset = new dataset()
da.fill(ds, "customers")

//c#
sqldataadapter da = new sqldataadapter("select * from customers; select * from orders;", myconnection);
dataset ds = new dataset();
da.fill(ds, "customers");


  来自customers表的数据放在名为“customers”的datatable中。来自orders表的数据放在名为“customers1”的datatable中。

  填充完dataset之后,能够很容易地将“customers1”表的tablename属性改为“orders”。但是,后面的填充会导致“customers”表被重新填充,而“orders”表会被忽略,并创建另外一个“customers1”表。为了对这种情况作出补救,创建一个datatablemapping,将“customers1”映射到“orders”,并为其他后面的表创建其他的表映射。例如:

visual basic
dim da as sqldataadapter = new sqldataadapter("select * from customers; select * from orders;", myconnection)
da.tablemappings.add("customers1", "orders")
dim ds as dataset = new dataset()
da.fill(ds, "customers")

//c#
sqldataadapter da = new sqldataadapter("select * from customers; select * from orders;", myconnection);
da.tablemappings.add("customers1", "orders");
dataset ds = new dataset();
da.fill(ds, "customers");


  使用datareader

  下面是一些使用datareader获得最好性能的技巧,同时还回答了一些关于使用datareader的常见问题。

  1) 在访问相关command的任何输出参数之前,必须关闭datareader。

  2) 完成读数据之后总是要关闭datareader。假如使用connection只是用于返回datareader,那么关闭datareader之后立即关闭他。

  另外一个显式关闭connection的方法是将commandbehavior.closeconnection传递给executereader方法,以确保相关的连接在关闭datareader时被关闭。假如从一个方法返回datareader,而且不能控制datareader或相关连接的关闭,则这样做特别有用。

  1) 不能在层之间远程访问datareader。datareader是为已连接好的数据访问设计的。

  2) 当访问列数据时,使用类型化访问器,例如,getstring、getint32等。这使您不用进行将getvalue返回的object强制转换成特定类型所需的处理。

  3) 一个单一连接每次只能打开一个datareader。在ado中,假如打开一个单一连接,并且请求两个使用只进、只读游标的记录集,那么ado会在游标生存期内隐式打开第二个、未池化的到数据存储区的连接,然后再隐式关闭该连接。对于ado.net,“秘密”完成的动作很少。假如想在相同的数据存储区上同时打开两个datareaders,就必须显式创建两个连接,每个datareader一个。这是ado.net为池化连接的使用提供更多控制的一种方法。

  4) 默认情况下,datareader每次read时都要将整行加载到内存。这允许在当前行内随机访问列。假如无需这种随机访问,为了提高性能,就将commandbehavior.sequentialaccess传递给executereader调用。这将datareader的默认行为更改为仅在请求时将数据加载到内存。注意,commandbehavior.sequentialaccess需要顺序访问返回的列。也就是说,一旦读过返回的列,就不能再读他的值了。

  5) 假如已完成读取来自datareader的数据,但仍然有大量挂起的未读结果,就在调用datareader的close之前先调用command的cancel。调用datareader的close会导致在关闭游标之前检索挂起的结果并清空流。调用command的cancel会放弃服务器上的结果,这样,datareader在关闭的时候就不必读这些结果。假如要从command返回输出参数,还要调用cancel放弃他们。假如需要读取任何输出参数,不要调用command的cancel,只要调用datareader的close即可。

  二进制大对象(blob)

  用datareader检索二进制大对象(blob)时,应该将commandbehavior.sequentialaccess传递给executereader方法调用。因为datareader的默认行为是每次read都将整行加载到内存,又因为blob值可能很大,所以结果可能由于单个blob而使大量内存被用光。sequentialaccess将datareader的行为配置为只加载请求的数据。然后还能够使用getbytes或getchars控制每次加载多少数据。

  记住,使用sequentialaccess时,不能不按顺序访问datareader返回的不同字段。也就是说,假如查询返回三列,其中第三列是blob,并且想访问前两列中的数据,就必须在访问blob数据之前先访问第一列的值,然后访问第二列的值。这是因为现在数据是顺序返回的,并且datareader一旦读过该数据,该数据就不再可用。

  使用命令

  ado.net提供了几种命令执行的不同方法连同优化命令执行的不同选项。下面包括一些技巧,他们是关于选择最好命令执行连同怎样提高执行命令的性能。

  使用oledbcommand的最好实践

  不同.net框架数据提供程式之间的命令执行被尽可能标准化了。但是,数据提供程式之间仍然存在差异。下面给出一些技巧,可微调用于ole db的.net框架数据提供程式的命令执行。

  1) 按照odbc call语法使用commandtype.text调用存储过程。使用commandtype.storedprocedure只是秘密地生成odbc call语法。

  2) 一定要配置oledbparameter的类型、大小(假如适用)、连同精度和范围(假如参数类型是numeric或decimal)。注意,假如不显式提供参数信息,oledbcommand会为每个执行命令重新创建ole db参数访问器。

  使用sqlcommand的最好实践

  使用sqlcommand执行存储过程的快速提示:假如调用存储过程,将sqlcommand的commandtype属性指定为storedprocedure的commandtype。这样通过将该命令显式标识为存储过程,就无需在执行之前分析命令。

  使用prepare方法

  对于重复作用于数据源的参数化命令,command.prepare方法能提高性能。prepare指示数据源为多次调用优化指定的命令。要想有效利用prepare,需要完全理解数据源是怎样响应prepare调用的。对于一些数据源(例如sql server 2000),命令是隐式优化的,不必调用prepare。对于其他(例如sql server 7.0)数据源,prepare会比较有效。

  显式指定架构和元数据

  只要用户没有指定元数据信息,ado.net的许多对象就会推断元数据信息。下面是一些示例:

  1) dataadapter.fill方法,假如dataset中没有表和列,dataadapter.fill方法会在dataset中创建表和列。

  2) commandbuilder,他会为单表select命令生成dataadapter命令属性。

  3) commandbuilder.deriveparameters,他会填充command对象的parameters集合。

  但是,每次用到这些特性,都会有性能损失。建议将这些特性主要用于设计时和即席应用程式中。在可能的情况下,显式指定架构和元数据。其中包括在dataset中定义表和列、定义dataadapter的command属性、连同为command定义parameter信息。

  executescalar和executenonquery

  假如想返回像count(*)、sum(price)或avg(quantity)的结果那样的单值,能够使用command.executescalar。executescalar返回第一行第一列的值,将结果集作为标量值返回。因为单独一步就能完成,所以executescalar不但简化了代码,还提高了性能;要是使用datareader就需要两步才能完成(即,executereader+取值)。

  使用不返回行的sql语句时,例如修改数据(例如insert、update或delete)或仅返回输出参数或返回值,请使用executenonquery。这避免了用于创建空datareader的任何不必要处理。

  测试null

  假如表(在数据库中)中的列允许为空,就不能测试参数值是否“等于”空。相反,需要写一个where子句,测试列和参数是否都为空。下面的sql语句返回一些行,他们的lastname列等于赋给@lastname参数的值,或lastname列和@lastname参数都为空。

select * from customers
where ((lastname = @lastname) or (lastname is null and @lastname is null))


  将null作为参数值传递

  对数据库的命令中,当将空值作为参数值发送时,不能使用null(visual basic .net中为nothing)。而需要使用dbnull.value。例如:

visual basic
dim param as sqlparameter = new sqlparameter("@name", sqldbtype.nvarchar, 20)
param.value = dbnull.value

//c#
sqlparameter param = new sqlparameter("@name", sqldbtype.nvarchar, 20);
param.value = dbnull.value;


  执行事务

  ado.net的事务模型已更改。在ado中,当调用starttransaction时,调用之后的任何更新操作都被视为是事务的一部分。但是,在ado.net中,当调用connection .begintransaction时,会返回一个transaction对象,需要将他和command的transaction属性联系起来。这种设计能够在一个单一连接上执行多个根事务。假如未将command.transaction属性配置为一个针对相关的connection而启动的transaction,那么command就会失败并引发异常。

  即将发布的.net框架将使您能够在现有的分布式事务中手动登记。这对于对象池方案来说很理想;在该方案中,一个池对象打开一次连接,但是在多个单独的事务中都涉及到该对象。.net框架1.0发行版中这一功能并不可用。

  使用连接

  高性能应用程式和使用中的数据源保持最短时间的连接,并且利用性能增强技术,例如连接池。下面的主题提供一些技巧,有助于在使用ado.net连接到数据源时获得更好的性能。

  连接池

  用于odbc的sql server、ole db和.net框架数据提供程式隐式缓冲连接。通过在连接字符串中指定不同的属性值,能够控制连接池的行为。

  用dataadapter优化连接

  dataadapter的fill和update方法在连接关闭的情况下自动打开为相关命令属性指定的连接。假如fill或update方法打开了连接,fill或update将在操作完成的时候关闭他。为了获得最好性能,仅在需要时将和数据库的连接保持为打开。同时,减少打开和关闭多操作连接的次数。

  假如只执行单个的fill或update方法调用,建议允许fill或update方法隐式打开和关闭连接。假如对fill和update调用有很多,建议显式打开连接,调用fill和update,然后显式关闭连接。

  另外,当执行事务时,显式地在开始事务之前打开连接,并在提交之后关闭连接。例如:

visual basic
public sub runsqltransaction(da as sqldataadapter, myconnection as sqlconnection, ds as dataset)
myconnection.open()
dim mytrans as sqltransaction = myconnection.begintransaction()
mycommand.transaction = mytrans

try
da.update(ds)
mytrans.commit()
console.writeline("update successful.")
catch e as exception
try
mytrans.rollback()
catch ex as sqlexception
if not mytrans.connection is nothing then
console.writeline("an exception of type " & ex.gettype().tostring() & " was encountered while attempting to roll back the transaction.")
end if
end try

console.writeline("an exception of type " & e.gettype().tostring() & " was encountered.")
console.writeline("update failed.")
end try
myconnection.close()
end sub

//c#
public void runsqltransaction(sqldataadapter da, sqlconnection myconnection, dataset ds)
{
myconnection.open();
sqltransaction mytrans = myconnection.begintransaction();
mycommand.transaction = mytrans;

try
{
da.update(ds);
mycommand.transaction.commit();
console.writeline("update successful.");
}
catch(exception e)
{
try
{
mytrans.rollback();
}
catch (sqlexception ex)
{
if (mytrans.connection != null)
{
console.writeline("an exception of type " + ex.gettype() +" was encountered while attempting to roll back the transaction.");
}
}

console.writeline(e.tostring());
console.writeline("update failed.");
}
myconnection.close();
}


  始终关闭connection和datareader

  完成对connection或datareader对象的使用后,总是显式地关闭他们。尽管垃圾回收最终会清除对象并因此释放连接和其他托管资源,但垃圾回收仅在需要时执行。因此,确保任何宝贵的资源被显式释放仍然是您的责任。并且,没有显式关闭的connections可能不会返回到池中。例如,一个超出作用范围却没有显式关闭的连接,只有当连接池大小达到最大并且连接仍然有效时,才会被返回到连接池中。

  注不要在类的finalize方法中对connection、datareader或任何其他托管对象调用close或dispose。最后完成的时候,仅释放类自己直接拥有的非托管资源。假如类没有任何非托管资源,就不要在类定义中包含finalize方法。

  在c#中使用“using”语句

  对于c#程式员来说,确保始终关闭connection和datareader对象的一个方便的方法就是使用using语句。using语句在离开自己的作用范围时,会自动调用被“使用”的对象的dispose。例如:

//c#
string connstring = "data source=localhost;integrated security=sspi;initial catalog=northwind;";

using (sqlconnection conn = new sqlconnection(connstring))
{
sqlcommand cmd = conn.createcommand();
cmd.commandtext = "select customerid, companyname from customers";

conn.open();

using (sqldatareader dr = cmd.executereader())
{
while (dr.read())
console.writeline("{0}\t{1}", dr.getstring(0), dr.getstring(1));
}
}


  using语句不能用于microsoft visual basic .net。
 
  避免访问oledbconnection.state属性

  假如连接已打开,oledbconnection.state属性会对dbprop_connectionstatus属性的datasourceinfo属性集执行本地ole db调用idbproperties.getproperties,这可能会导致对数据源的往返行程。也就是说,检查state属性的代价可能很高。所以仅在需要时检查state属性。假如需要经常检查该属性,监听oledbconnection的statechange事件可能会使应用程式的性能好一些。

  和xml集成

  ado.net在dataset中提供了广泛的xml集成,并公开了sql server 2000及其更高版本提供的部分xml功能。还能够使用sqlxml 3.0广泛地访问sql server 2000及其更高版本中的xml功能。下面是使用xml和ado.net的技巧和信息。

  dataset和xml

  dataset和xml紧密集成,并提供如下功能:

  1) 从xsd架构中加载dataset的架构或关系型结构。

  2) 从xml加载dataset的内容。

  3) 假如没有提供架构,能够从xml文档的内容推断出dataset的架构。

  4) 将dataset的架构写为xsd架构。

  5) 将dataset的内容写为xml。

  6) 同步访问使用dataset的数据的关系表示,连同使用xmldatadocument的数据的层次表示。

  注能够使用这种同步将xml功能(例如,xpath查询和xslt转换)应用到dataset中的数据,或在保留原始xml保真度的前提下为xml文档中数据的全部或其中一个子集提供关系视图。

  架构推断

  从xml文档加载dataset时,能够从xsd架构加载dataset架构,或在加载数据前预定义表和列。假如没有可用的xsd架构,而且不知道为xml文档的内容定义哪些表和列,就能够在xml文档结构的基础上对架构进行推断。

  架构推断作为迁移工具很有用,但应只限于设计阶段应用程式,这是由于推断处理有如下限制。

  1) 对架构的推断会引入影响应用程式性能的附加处理。

  2) 任何推断列的类型都是字符串。

  3) 推断处理不具备确定性。也就是说,他是基于xml文档内容的,而不是预定的架构。因此,对于两个预定架构相同的xml文档,由于他们的内容不同,结果得到两个完全不同的推断架构。

  用于xml查询的sql server

  假如正从sql server 2000 for xml返回查询结果,能够让用于sql server的.net框架数据提供程式使用sqlcommand.executexmlreader方法直接创建一个xmlreader。

  sqlxml托管类

  .net框架中有一些类,公开用于sql server 2000的xml的功能。这些类可在microsoft.data.sqlxml命名空间中找到,他们添加了执行xpath查询和xml模板文档连同将xslt转换应用到数据的能力。

  sqlxml托管类包含在用于microsoft sql server 2000的xml (sqlxml 2.0)发行版中,可通过链接xml for microsoft sql server 2000 web release 2 (sqlxml 2.0)

  更多有用的技巧

  下面是一些编写ado.net代码时的通用技巧。

  避免自动增量值冲突

  就像大多数数据源相同,dataset使您可标识那些添加新行时自动对其值进行递增的列。在dataset中使用自动增量的列时,假如自动增量的列来自数据源,可避免添加到dataset的行和添加到数据源的行之间本地编号冲突。

  例如,考虑一个表,他的主键列customerid是自动增量的。两个新的客户信息行添加到表中,并接收到自动增量的customerid值1和2。然后,只有第二个客户行被传递给dataadapter的方法update,新添加的行在数据源接收到一个自动增量的customerid值1,和dataset中的值2不匹配。当dataadapter用返回值填充表中第二行时,就会出现约束冲突,因为第一个客户行已使用了customerid值1。

  要避免这种情况,建议在使用数据源上自动增量的列连同dataset上自动增量的列时,将dataset中的列创建为autoincrementstep值等于-1并且autoincrementseed值等于0,另外,还要确保数据源生成的自动增量标识值从1开始,并且以正阶值递增。因此,dataset为自动增量值生成负数,和数据源生成的正自动增量值不冲突。另外一个选择是使用guid类型的列,而不是自动增量的列。生成guid值的算法应该永远不会使数据源中生成的guid值和dataset中生成的guid值相同。

  假如自动增量的列只是用作唯一值,而且没有任何意义,就考虑使用guid代替自动增量的列。他们是唯一的,并且避免了使用自动增量的列所必需的额外工作。

  检查开放式并发冲突

  按照设计,由于dataset是和数据源断开的,所以,当多个客户端在数据源上按照开放式并发模型更新数据时,需要确保应用程式避免冲突。

  在测试开放式并发冲突时有几项技术。一项技术涉及在表中包含时间戳列。另外一项技术是,验证一行中任何列的原始值是否仍然和通过在sql语句中使用where子句进行测试时在数据库中找到的值相匹配。

  多线程编程

  ado.net对性能、吞吐量和可伸缩性进行优化。因此,ado.net对象不锁定资源,并且必须只用于单线程。一个例外是dataset,他对多个阅读器是线程安全的。但是,在写的时候需要将dataset锁定。

  仅在需要的时候才用com interop访问ado

  ado.net的设计目的是成为许多应用程式的最好解决方案。但是,有些应用程式需要只有使用ado对象才有的功能,例如,ado多维(adomd)。在这些情况下,应用程式能够用com interop访问ado。注意使用com interop访问具备ado的数据会导致性能降低。在设计应用程式时,首先在实现用com interop访问ado的设计之前,先确定ado.net是否满足设计需求。

喜欢本文,那就收藏到:

    Del.icio.us Google书签 Digg Live Bookmark Technorati Furl Yahoo书签 Facebook 百度搜藏 新浪ViVi 365Key网摘 天极网摘 和讯网摘 博拉网 POCO网摘 添加到饭否 QQ书签 Digbuzz我挖网
相关评论  我也要评论
还没有关于此文章的相关评论!
  • 昵称: (为空则显示guest)
  • 评论分数: ★ ★ ★★★ ★★★★ ★★★★★
  • 评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
  • 导航
    赞助商
    文章类别
    订阅