更新时间:2024-01-06 22:21
远程数据服务(Remote Data Services,RDS)是允许我们处理客户端数据的一系列服务的统称。不用担心这方面的问题,因为RDS本身就是ADO的一部分,只有在需要传送和使用客户端数据时,才会使用。
远程数据服务(Remote Data Services,RDS)是允许我们处理客户端数据的一系列服务的统称。不用担心这方面的问题,因为RDS本身就是ADO的一部分,只有在需要传送和使用客户端数据时,才会使用。
远程数据服务RDS允许程序员开发原生的WINDOWS分布式多层应用系统,或是开发以浏览器为图形用户接口的WEB应用系统。
远程数据服务RDS提供了客户端应用程序在INTERNET/INTRANET或分布式环境中使用ADO中RECORDSET对象的能力。
可以在浏览器中通过远程数据服务RDS取得RECORDSET对象,然后在脚本语言中存取数据。或在原生WINDOWS应用程序中通过RDS取得RECORDSET对象,然后使用程序代码来存取远程数据源中的数据。RDS能够将ADO取得的数据一DCOM或HTTP通信协议由中介软件或中介组件传递给客户端,并且把数据缓存在客户端中让客户端存取数据。
远程数据服务RDS和ADO的关系:
程序ADORDSIIS/PWSODBC数据库.
当我们在程序中试图使用ADO来存取WEB数据库时,由于ADO与ODBC分属于两台通过Internet连接起来的机器上,因此数据存取方式与ADO,ODBC同属于一台机器的情况大不相同,为了让程序也一样可以利用ADO存取WEB数据库,于是诞生了RDS,而RDS的角色就象是一位帮ADO存取WEB数据库的服务员一样,所以取名“远程端数据服务”
实际上RDS是由几个组件构成的。图10-1说明了这些组件以及它们之间是如何协同工作的。
组件似乎很多,但并不是所有的组件在每种情形下都被使用,实际上有一些不是RDS的一部分。然而这里还是把所有可能出现的组件都放在了图上,以备需要时查看。图10-1分成了两部分,因为使用客户端数据需要一些向客户端传送数据的方法,同时数据一旦到达客户端,也需要一些管理数据的方法。我们先从服务器端开始。
10.2.1 RDS服务器组件
虽然RDS用于传送和访问客户端数据,但其确实有一些基于服务器的组件。这是必需的,因为肯定需要某种方式将数据传送到客户端。因此有了一系列能访问数据并允许发送数据到客户端的服务器组件。我们把实际的数据传送称为调度(marshal)。
服务器端组件图的最上端是数据存储,由OLE DB提供者访问。它并不是RDS的一部分,但这表示只要有相应的OLE DB提供者,就可以通过RDS在客户端使用任何数据。至于如何处理服务器上的数据,可以有两种选择:
·数据工厂(datafactory)是缺省的用于访问数据存储的服务器端组件。它作为服务器端RDS组件的一部分安装在计算机上,除了能从数据存储中获取数据外,还为服务器处理发送到客户端以及从客户端发送来的数据。
· 自定义组件只是一个普通的提供了数据传送方法的COM组件。当数据工厂不能提供所需的功能时,可以使用自定义组件。本章将介绍一个简单的组件例子,在本书的后面还有一个更复杂的例子。
Web服务器使用这两种组件作为客户和服务器数据的接口。
10.2.2 RDS客户组件
在客户端先从底端的DataSpace对象开始,该对象作为客户端的一部分与数据工厂或自定义对象协同工作。DataSpace对象是一个代理对象,负责与服务器进行通信,同时也是数据传输的通道(或者通常所说的调度)。DataSpace对象是用客户端脚本语言或用HTML语言中的 标记创建的COM对象。在本章后面会看到关于这方面的例子。
DataSpace对象上面是数据源对象(Data Source Object,DSO),负责存储客户端数据。一个数据源对象包含一个ADO数据记录集,与客户数据缓存共同管理数据。客户数据缓存只是一种管理客户端数据的客户光标服务。同时数据源对象又是一个COM对象,与DataSpace对象类似,也可以通过客户端脚本或使用HTML语言中的 标记来创建。同样,在本章稍后也会介绍关于这方面的一些例子。
数据源对象的上面是数据绑定管理器,任务是建立HTML控件与数据源对象的连接。这就是我们所知道的绑定,可以通过设置某些HTML控件的DATASRC和DATAFLD属性来实现。下面将对这些内容进行讨论,并示范如何在浏览器中方便地使用数据。
10.2.3 支持RDS的浏览器
要知道RDS是微软的技术,因此只能在微软的浏览器上工作。实际上,只有在IE 4.0或更高版本的浏览器中才完全支持RDS。
当编写依赖于RDS的应用程序时,需要注意访问应用程序的客户的RDS版本可能与服务器端有所不同。举例来说,IE 4中的是RDS 1.5版本,而IE 5、Office 2000和Visual Studio 6中的则是RDS 2.0版本。有两种方法可以处理这种兼容性问题:
· 确保所有用户已经升级到RDS的最新版本。如果客户运行的是Windows 2000,那么已经在运行最新版本的RDS了。否则,可以从网址下载。RDS 2.5版本是最新的随同Windows 2000一起发布的版本,同时也是一个可单独下载的软件包。
· 当连接到数据源时,指定数据工厂的模式。这可以指定使用的是哪一个版本的RDS组件,后面将介绍这方面的一个例子。
10.2.4 数据源对象
数据源对象是一个存储和管理客户端数据的客户端对象。因为这是使用RDS最简单的一种方式,首先研究一下这些对象。
这里有几个不同的数据源对象,每一个都针对不同类型的数据:
· 表格数据控件(Tabular Data Control,TDC),用于处理表格形式或分隔形式的文本文件。
· RDS数据控件,用于连接OLE DB数据存储,能够指定连接到哪个数据存储,以及返回哪些数据。
· Java数据库连接器,这是一个通过Java数据库控件(Java DataBase Control,JDBC)连接到数据存储的Java小程序。这里我们不想讨论JDBC,因为它并不提供其他控件无法实现的功能。
· 微软的HTML(MSHTML)数据源对象用HTML标记数据,并把它作为数据源。
· XML数据源对象使用XML数据,用于结构化的或任意结构的XML。
选用哪一种数据源对象取决于你想做什么,以及数据从哪里来。如果需要向客户提供少量的数据,并且不允许用户修改数据,那么表格数据控件(TDC)可能会比较适合。这种数据源是一个文本文件,不需要任何数据库,因此编辑起来比较简单。对于从数据库中取出数据并且可能需要更新的情况,RDS数据控件是最合适的。而对于许多新数据源,会发现此时需要使用XML数据控件。这实际依赖于所使用的Web应用程序的类型,以及用户所需的功能。
我们将依次介绍这些数据控件,一旦了解了如何用它们把数据传送到客户端,将会介绍如何使用这些数据。
1. 表格数据控件
表格数据控件(Tabular Data Control,TDC)是最简单的数据源对象,主要用于少量的只读数据,特别是那些从不改变或很少修改的,不需要从客户端进行更新的静态数据。例如,表格数据控件能提供一个网页内的菜单项或链接的列表。
通过在HTML代码中使用 标记可以创建一个表格数据控件。参数DataURL可以指定包含文本数据的文件名。
TDC只读取表格中的数据或标记为表格形式的数据,例如,可以处理逗号分隔形式的数据(Comma Separated Value, CSV),类似于下面的数据:
TDC也可以自由定义。除了DataURL外,TDC还有16个参数,可以通过设置OBJECT标记的参数项或编写脚本代码来配置这些参数。参数的说明如表10-1所示:
下面是使用参数创建TDC的一个例子。
也可以在客户端脚本中获取数据,下面的例子显示了给TDC加载数据的JScript脚本。
function fillTDC()
{
dsoAuthors.dataURL = 'authors.csv';
dsoAuthors.Reset();
}
如果改变了TDC的DataURL参数,必须使用Reset方法,这样才能使新的URL起作用。当介绍数据绑定时,会更详细地讨论如何使用它。Reset方法是TDC唯一的一个方法。2. RDS数据控件RDS数据控件能够访问一般的数据存储,而不是平面文件。它通常用于连接SQL数据库以从表、查询或存储过程获取数据。与TDC不同,RDS数据控件允许更新数据。在本章稍后通过示例说明如何进行数据更新。
类似于TDC,可以用HTML脚本中的OBJECT标记来创建一个RDS数据控件,并以类似的方式设置其属性。
authors
下面创建虚表。
这充当了模板的作用。注意,表格中还没有单元格。这是因为并不知道数据有多少个字段,所以也将在运行期间创建它们。
编写JScript代码。首先看一下resetData函数,该函数设置数据控件的属性并加载数据。
function resetData(sTable)
{
// reset the data
dsoData.Connect = 'Provider=SQLOLEDB; Data Source=' +
'' +
'; Initial Catalog=pubs; User ID=sa; Password=';
dsoData.Server = 'http://';
dsoData.SQL = 'SELECT * FROM ' + sTable;
dsoData.Refresh();
}虽然这看起来比使用参数更复杂一些,但是仍然比较简单。别忘了参数名是如何映射到属性的?这里所做的就是设置那些属性,然后调用Refresh方法更新数据控件。看上去,这可能比以前的例子更糟糕,因为在代码中只有不多的ASP,也只是简单地在属性中填入Web服务器的名字。但使用该方法可以在不修改代码的情况下将此ASP页面从一个服务器移到另一个服务器。作为数据源的表名可以通过选择适当的按钮而传给函数。
一旦加载了数据,将触发数据控件的ondatasetcomplete事件,运行createCells函数。
function createCells()
{
var fldF;
var tblCell; // delete what's there already
deleteCells(); // now create the new cells
for (fldF = new enumerator(dsoData.recordset.Fields);
!fldF.atEnd(); fldF.moveNext())
{
// create a new cell for the heading
tblCell = tblData.rows【0】.insertCell();
tblCell.innerHTML = '' + fldF.item().name + ''; // create a new cell for the body
tblCell = tblData.rows【1】.insertCell();
tblCell.innerHTML = '';
} // now bind to the data source
tblData.dataSrc = '#dsoData';
}
这同样也很简单。首先删除了现有的表格单元格(马上会介绍这个函数),然后遍历记录集的字段。在行头为每个字段创建一个新单元格(这个表格只有两行:第一行,即第0行,是表头;第二行,即第1行,是表体)。表格单元创建完后,将innerHTML属性设为对应的字段名。在表体中创建新单元格的过程类似,但此时使用innerHTML元件保存绑定到数据字段的INPUT标记。当所有的字段都完成这样的操作后,这个表就与数据控件绑定了。
因为这个页面允许在两个不同的数据集之间进行切换,所以需要先删除现有的数据。
function deleteCells()
{
var iCell;
var iCells; // unbind the table
tblData.dataSrc = ''; // delete existing cells
iCells = tblData.rows【0】.cells.length
for (iCell = 0; iCell < iCells; ++iCell)
{
tblData.rows【0】.deleteCell();
tblData.rows【1】.deleteCell();
}
}
这个子程序只是对表解除绑定,然后在表格中遍历所有的单元格并删除它们。等到上述程序执行完毕,表格就只剩下空的表头和表体行。
这是一个用RDS和一些DHTML实现的简单例子。可以容易地把其加到一个ASP包含文件中,并把该文件放到任何应用程序中,即使数据源不改变也可使用这种方法。
这个例子的全部代码——文件RDSDynamicBinding.asp以及类似的其他类型的数据控件例子,可以在Wrox站点上找到。10.2.6 更新数据
迄今为止,仅学习了在客户端如何取到数据,但还没有涉及如何更新客户端数据,和将其送回服务器。别忘了,记录集是断开连接的,那么如何更新数据呢?对数据所做的任何修改只是数据控件中本地记录的一部分,因此为了更新服务器必须发一条特殊的指令。然而这并不需做什么复杂的工作,因为RDS数据控件有两个方法,允许我们要么取消最近对数据所做的任何修改,要么将所有修改送到服务器。
为了方便用户,可以为此创建一些按钮。
>Cnacel
>Save
SubmitChanges方法只将那些改动过的记录送回服务器,而CancelUpdate方法则取消在本地记录集上所做的任何修改。
更新和取消更新操作并不是唯一所需的。如果想增加新的记录或删除一条现有的记录,怎么办?可以使用记录集的AddNew和Delete方法。这将增加或删除记录集中的记录,然后在发送SubmitChanges命令后,服务器上的数据就可以被更新。
>Delete
>Add
1. 解决冲突的方法
由于与数据源断开连接,可能会碰到有关冲突的问题。例如在更新一条记录并将其保存到数据存储的时候,有人也修改了这条记录时,会发生什么情况? SubmitChanges方法已经提供了相应的处理冲突的方法,如果发生冲突,那么该方法将产生一个错误。
在调用SubmitChanges方法期间,只要其中一条记录更新失败,那么所有的记录更新都会失败。这保证了原始数据不会被部分更新。可以遍历记录集,并检测记录的Status属性来告诉用户哪一条记录更新失败了。例如,最好调用自己的UpdateData函数,而不只是在命令按钮中调用SubmitChanges方法。 此时,我们知道已经发生了一个错误,但并不知道是哪一个错误,因此必须重新同步当前数据与数据存储中的数据。使用adResyncUnderlyingValues确保只有字段的UnderlyingValues属性被数据存储中的值覆盖,也就是说所做的修改是安全的(记住,修改的内容保存在Value属性中)。可以在后面的代码中比较当前的值与数据库中的值。 Status可以是不同值的组合,详见附录。例子代码( RDSConflicts.asp )中有一个将这些值转换为描述性字符串的函数。
我们知道记录有某些形式的冲突,但无法确切地知道为什么或哪一个字段引起了冲突。因此需要遍历字段检测它们的值。 这就是UnderlyingVaule属性发挥作用的地方。
字段有三种值:
Vaule代表新值,即经过修改的字段值。
UnderlyingVaule代表数据存储中存储的字段值。
OriginalVaule代表从数据存储读取后,但还没有修改之前的字段值。
这意味着UnderlyingVaule会保存其他用户修改过的值,而OriginalValue是字段原有的值。因此比较两者之值,如果不同,则说明字段已经被另外的用户修改了。
可以利用所有这些错误信息来创建一个表格以显示是否确实发生错误。例子(RDSConflicts.asp)产生的输出结果如图1 0 - 11所示。 这里可以见到三种不同的值。原始值是Johnson。然后,在另一个窗口(如SQL Server Query Analyzer)中将值改为Johnson。在浏览器窗口,利用RDS将这个值改为Andy,并按下Save All Changes按钮。Resync命令将数据库中的值取出并写入UnderlyingVaule属性。我也对Lastname列做了相似的修改。
使用这种方法,可以看到每一个发生变化的字段的值。由于SubmitChanges方法可以处理多个字段,读者可能希望为这个表增加额外的列以显示ID字段,这样就可以看到是哪一个字段更新失败了。
下面是使用参数创建TDC的一个例子。
也可以在客户端脚本中获取数据,下面的例子显示了给TDC加载数据的JScript脚本。
function fillTDC()
{
dsoAuthors.dataURL = 'authors.csv';
dsoAuthors.Reset();
}
如果改变了TDC的DataURL参数,必须使用Reset方法,这样才能使新的URL起作用。当介绍数据绑定时,会更详细地讨论如何使用它。Reset方法是TDC唯一的一个方法。
2. RDS数据控件RDS数据控件能够访问一般的数据存储,而不是平面文件。它通常用于连接SQL数据库以从表、查询或存储过程获取数据。与TDC不同,RDS数据控件允许更新数据。在本章稍后通过示例说明如何进行数据更新。
类似于TDC,可以用HTML脚本中的OBJECT标记来创建一个RDS数据控件,并以类似的方式设置其属性。
authors
下面创建虚表。
这充当了模板的作用。注意,表格中还没有单元格。这是因为并不知道数据有多少个字段,所以也将在运行期间创建它们。
编写JScript代码。首先看一下resetData函数,该函数设置数据控件的属性并加载数据。
function resetData(sTable)
{
// reset the data
dsoData.Connect = 'Provider=SQLOLEDB; Data Source=' +
'' +
'; Initial Catalog=pubs; User ID=sa; Password=';
dsoData.Server = 'http://';
dsoData.SQL = 'SELECT * FROM ' + sTable;
dsoData.Refresh();
}虽然这看起来比使用参数更复杂一些,但是仍然比较简单。别忘了参数名是如何映射到属性的?这里所做的就是设置那些属性,然后调用Refresh方法更新数据控件。看上去,这可能比以前的例子更糟糕,因为在代码中只有不多的ASP,也只是简单地在属性中填入Web服务器的名字。但使用该方法可以在不修改代码的情况下将此ASP页面从一个服务器移到另一个服务器。作为数据源的表名可以通过选择适当的按钮而传给函数。
一旦加载了数据,将触发数据控件的ondatasetcomplete事件,运行createCells函数。
function createCells()
{
var fldF;
var tblCell; // delete what's there already
deleteCells(); // now create the new cells
for (fldF = new enumerator(dsoData.recordset.Fields);
!fldF.atEnd(); fldF.moveNext())
{
// create a new cell for the heading
tblCell = tblData.rows【0】.insertCell();
tblCell.innerHTML = '' + fldF.item().name + ''; // create a new cell for the body
tblCell = tblData.rows【1】.insertCell();
tblCell.innerHTML = '';
} // now bind to the data source
tblData.dataSrc = '#dsoData';
}
这同样也很简单。首先删除了现有的表格单元格(马上会介绍这个函数),然后遍历记录集的字段。在行头为每个字段创建一个新单元格(这个表格只有两行:第一行,即第0行,是表头;第二行,即第1行,是表体)。表格单元创建完后,将innerHTML属性设为对应的字段名。在表体中创建新单元格的过程类似,但此时使用innerHTML元件保存绑定到数据字段的INPUT标记。当所有的字段都完成这样的操作后,这个表就与数据控件绑定了。
因为这个页面允许在两个不同的数据集之间进行切换,所以需要先删除现有的数据。
function deleteCells()
{
var iCell;
var iCells; // unbind the table
tblData.dataSrc = ''; // delete existing cells
iCells = tblData.rows【0】.cells.length
for (iCell = 0; iCell < iCells; ++iCell)
{
tblData.rows【0】.deleteCell();
tblData.rows【1】.deleteCell();
}
}
这个子程序只是对表解除绑定,然后在表格中遍历所有的单元格并删除它们。等到上述程序执行完毕,表格就只剩下空的表头和表体行。
这是一个用RDS和一些DHTML实现的简单例子。可以容易地把其加到一个ASP包含文件中,并把该文件放到任何应用程序中,即使数据源不改变也可使用这种方法。
这个例子的全部代码——文件RDSDynamicBinding.asp以及类似的其他类型的数据控件例子,可以在Wrox站点上找到。10.2.6 更新数据
迄今为止,仅学习了在客户端如何取到数据,但还没有涉及如何更新客户端数据,和将其送回服务器。别忘了,记录集是断开连接的,那么如何更新数据呢?对数据所做的任何修改只是数据控件中本地记录的一部分,因此为了更新服务器必须发一条特殊的指令。然而这并不需做什么复杂的工作,因为RDS数据控件有两个方法,允许我们要么取消最近对数据所做的任何修改,要么将所有修改送到服务器。
为了方便用户,可以为此创建一些按钮。
>Cnacel
>Save
SubmitChanges方法只将那些改动过的记录送回服务器,而CancelUpdate方法则取消在本地记录集上所做的任何修改。
更新和取消更新操作并不是唯一所需的。如果想增加新的记录或删除一条现有的记录,怎么办?可以使用记录集的AddNew和Delete方法。这将增加或删除记录集中的记录,然后在发送SubmitChanges命令后,服务器上的数据就可以被更新。
>Delete
>Add
1. 解决冲突的方法
由于与数据源断开连接,可能会碰到有关冲突的问题。例如在更新一条记录并将其保存到数据存储的时候,有人也修改了这条记录时,会发生什么情况? SubmitChanges方法已经提供了相应的处理冲突的方法,如果发生冲突,那么该方法将产生一个错误。
在调用SubmitChanges方法期间,只要其中一条记录更新失败,那么所有的记录更新都会失败。这保证了原始数据不会被部分更新。可以遍历记录集,并检测记录的Status属性来告诉用户哪一条记录更新失败了。例如,最好调用自己的UpdateData函数,而不只是在命令按钮中调用SubmitChanges方法。 此时,我们知道已经发生了一个错误,但并不知道是哪一个错误,因此必须重新同步当前数据与数据存储中的数据。使用adResyncUnderlyingValues确保只有字段的UnderlyingValues属性被数据存储中的值覆盖,也就是说所做的修改是安全的(记住,修改的内容保存在Value属性中)。可以在后面的代码中比较当前的值与数据库中的值。 Status可以是不同值的组合,详见附录。例子代码( RDSConflicts.asp )中有一个将这些值转换为描述性字符串的函数。
我们知道记录有某些形式的冲突,但无法确切地知道为什么或哪一个字段引起了冲突。因此需要遍历字段检测它们的值。 这就是UnderlyingVaule属性发挥作用的地方。
字段有三种值:
Vaule代表新值,即经过修改的字段值。
UnderlyingVaule代表数据存储中存储的字段值。
OriginalVaule代表从数据存储读取后,但还没有修改之前的字段值。
这意味着UnderlyingVaule会保存其他用户修改过的值,而OriginalValue是字段原有的值。因此比较两者之值,如果不同,则说明字段已经被另外的用户修改了。
可以利用所有这些错误信息来创建一个表格以显示是否确实发生错误。例子(RDSConflicts.asp)产生的输出结果如图1 0 - 11所示。 这里可以见到三种不同的值。原始值是Johnson。然后,在另一个窗口(如SQL Server Query Analyzer)中将值改为Johnson。在浏览器窗口,利用RDS将这个值改为Andy,并按下Save All Changes按钮。Resync命令将数据库中的值取出并写入UnderlyingVaule属性。我也对Lastname列做了相似的修改。
使用这种方法,可以看到每一个发生变化的字段的值。由于SubmitChanges方法可以处理多个字段,读者可能希望为这个表增加额外的列以显示ID字段,这样就可以看到是哪一个字段更新失败了。
2. RDS数据控件
RDS数据控件能够访问一般的数据存储,而不是平面文件。它通常用于连接SQL数据库以从表、查询或存储过程获取数据。与TDC不同,RDS数据控件允许更新数据。在本章稍后通过示例说明如何进行数据更新。
类似于TDC,可以用HTML脚本中的OBJECT标记来创建一个RDS数据控件,并以类似的方式设置其属性。
authors
下面创建虚表。
这充当了模板的作用。注意,表格中还没有单元格。这是因为并不知道数据有多少个字段,所以也将在运行期间创建它们。
编写JScript代码。首先看一下resetData函数,该函数设置数据控件的属性并加载数据。
function resetData(sTable)
{
// reset the data
dsoData.Connect = 'Provider=SQLOLEDB; Data Source=' +
'' +
'; Initial Catalog=pubs; User ID=sa; Password=';
dsoData.Server = 'http://';
dsoData.SQL = 'SELECT * FROM ' + sTable;
dsoData.Refresh();
}
虽然这看起来比使用参数更复杂一些,但是仍然比较简单。别忘了参数名是如何映射到属性的?这里所做的就是设置那些属性,然后调用Refresh方法更新数据控件。看上去,这可能比以前的例子更糟糕,因为在代码中只有不多的ASP,也只是简单地在属性中填入Web服务器的名字。但使用该方法可以在不修改代码的情况下将此ASP页面从一个服务器移到另一个服务器。作为数据源的表名可以通过选择适当的按钮而传给函数。
一旦加载了数据,将触发数据控件的ondatasetcomplete事件,运行createCells函数。
function createCells()
{
var fldF;
var tblCell;
// delete what's there already
deleteCells();
// now create the new cells
for (fldF = new enumerator(dsoData.recordset.Fields);
!fldF.atEnd(); fldF.moveNext())
{
// create a new cell for the heading
tblCell = tblData.rows【0】.insertCell();
tblCell.innerHTML = '' + fldF.item().name + '';
// create a new cell for the body
tblCell = tblData.rows【1】.insertCell();
tblCell.innerHTML = '';
}
// now bind to the data source
tblData.dataSrc = '#dsoData';
}
这同样也很简单。首先删除了现有的表格单元格(马上会介绍这个函数),然后遍历记录集的字段。在行头为每个字段创建一个新单元格(这个表格只有两行:第一行,即第0行,是表头;第二行,即第1行,是表体)。表格单元创建完后,将innerHTML属性设为对应的字段名。在表体中创建新单元格的过程类似,但此时使用innerHTML元件保存绑定到数据字段的INPUT标记。当所有的字段都完成这样的操作后,这个表就与数据控件绑定了。
因为这个页面允许在两个不同的数据集之间进行切换,所以需要先删除现有的数据。
function deleteCells()
{
var iCell;
var iCells;
// unbind the table
tblData.dataSrc = '';
// delete existing cells
iCells = tblData.rows【0】.cells.length
for (iCell = 0; iCell < iCells; ++iCell)
{
tblData.rows【0】.deleteCell();
tblData.rows【1】.deleteCell();
}
}
这个子程序只是对表解除绑定,然后在表格中遍历所有的单元格并删除它们。等到上述程序执行完毕,表格就只剩下空的表头和表体行。
这是一个用RDS和一些DHTML实现的简单例子。可以容易地把其加到一个ASP包含文件中,并把该文件放到任何应用程序中,即使数据源不改变也可使用这种方法。
这个例子的全部代码——文件RDSDynamicBinding.asp以及类似的其他类型的数据控件例子,可以在Wrox站点上找到。
10.2.6 更新数据
迄今为止,仅学习了在客户端如何取到数据,但还没有涉及如何更新客户端数据,和将其送回服务器。别忘了,记录集是断开连接的,那么如何更新数据呢?对数据所做的任何修改只是数据控件中本地记录的一部分,因此为了更新服务器必须发一条特殊的指令。然而这并不需做什么复杂的工作,因为RDS数据控件有两个方法,允许我们要么取消最近对数据所做的任何修改,要么将所有修改送到服务器。
为了方便用户,可以为此创建一些按钮。
>Cnacel
>Save
SubmitChanges方法只将那些改动过的记录送回服务器,而CancelUpdate方法则取消在本地记录集上所做的任何修改。
更新和取消更新操作并不是唯一所需的。如果想增加新的记录或删除一条现有的记录,怎么办?可以使用记录集的AddNew和Delete方法。这将增加或删除记录集中的记录,然后在发送SubmitChanges命令后,服务器上的数据就可以被更新。
>Delete
>Add
1. 解决冲突的方法
由于与数据源断开连接,可能会碰到有关冲突的问题。例如在更新一条记录并将其保存到数据存储的时候,有人也修改了这条记录时,会发生什么情况? SubmitChanges方法已经提供了相应的处理冲突的方法,如果发生冲突,那么该方法将产生一个错误。
在调用SubmitChanges方法期间,只要其中一条记录更新失败,那么所有的记录更新都会失败。这保证了原始数据不会被部分更新。可以遍历记录集,并检测记录的Status属性来告诉用户哪一条记录更新失败了。例如,最好调用自己的UpdateData函数,而不只是在命令按钮中调用SubmitChanges方法。
此时,我们知道已经发生了一个错误,但并不知道是哪一个错误,因此必须重新同步当前数据与数据存储中的数据。使用adResyncUnderlyingValues确保只有字段的UnderlyingValues属性被数据存储中的值覆盖,也就是说所做的修改是安全的(记住,修改的内容保存在Value属性中)。可以在后面的代码中比较当前的值与数据库中的值。
Status可以是不同值的组合,详见附录。例子代码( RDSConflicts.asp )中有一个将这些值转换为描述性字符串的函数。
我们知道记录有某些形式的冲突,但无法确切地知道为什么或哪一个字段引起了冲突。因此需要遍历字段检测它们的值。
这就是UnderlyingVaule属性发挥作用的地方。
字段有三种值:
Vaule代表新值,即经过修改的字段值。
UnderlyingVaule代表数据存储中存储的字段值。
OriginalVaule代表从数据存储读取后,但还没有修改之前的字段值。
这意味着UnderlyingVaule会保存其他用户修改过的值,而OriginalValue是字段原有的值。因此比较两者之值,如果不同,则说明字段已经被另外的用户修改了。
可以利用所有这些错误信息来创建一个表格以显示是否确实发生错误。例子(RDSConflicts.asp)产生的输出结果如图1 0 - 11所示。
这里可以见到三种不同的值。原始值是Johnson。然后,在另一个窗口(如SQL Server Query Analyzer)中将值改为Johnson。在浏览器窗口,利用RDS将这个值改为Andy,并按下Save All Changes按钮。Resync命令将数据库中的值取出并写入UnderlyingVaule属性。我也对Lastname列做了相似的修改。
使用这种方法,可以看到每一个发生变化的字段的值。由于SubmitChanges方法可以处理多个字段,读者可能希望为这个表增加额外的列以显示ID字段,这样就可以看到是哪一个字段更新失败了。