以我们前面讲到的情况为例,在郑某编辑第2条记录时,getfldstate(-1)将返回"112",第一个数字"1"说明记录没有添加或删除,第二个数字"1"说明第一个字段(姓名)没有改变,第三个数字"2"说明第二个字段(公司名称)内容改变了。
缓冲记录的写回
我们还继续刚才的例子。现在假设郑某点击了"保存"按钮,我们应该怎样将缓冲中的数据写到记录中(更新表)呢?对于行缓冲来说,当你移动记录指针或调用tableupdate()函数时,表就会被更新。对于表缓冲来说,移动记录指针并不会引起表的更新(因为它是多记录被同时缓冲),所以通常情况下只能调用tableupdate()函数来更新表。对于行缓冲最好也用tableupdate()函数,因为这样更好地控制程序的去向。
如果缓冲器中的内容被正确地写入到记录中,tableupdate()返回.T.值,如果记录缓冲没有改变(用户没有编辑任何字段、添加记录或改变记录的删除状态),此时,tableupdate()也返回.T.,尽管实际上什么也没有做。
Tableupdate()可以带几个参数:
Tableupdate( <AllRows>,<Forced>,<Alias>|<Workarea> )
第一个参数指明哪些记录被更新:设为.F.,则只更新当前记录,若为.T.,则更新所有记录(仅影响表缓冲)。如果第二个参数是.T.,那么其它用户的任何修改将被当前用户的修改所覆盖。如果没定义第三个参数,tableupdate()将更新当前表。
怎样取消用户所做的修改呢?对于用内存变量的方法,可以再次用scatter memevar 语句从磁盘上的数据恢复到内存变量中,而对于缓冲来说,用tablerevert()函数即可达到同样功能。
错误处理
我们继续"郑某和于某"的例子,当郑某点击"保存"按钮后,代码将执行tableupdate()函数以把缓冲中的数据写入记录。请记住,在郑某编辑记录时于某已经修改了同一条记录并做了保存。当郑某点击"保存"时,tableupdate() 将返回.F.,说明它不能将缓冲写入记录中,为什么会是这样呢?
VFP在以下几种情况下无法将缓冲写入记录:
l 当一个用户编辑记录时,其它用户修改并保存了该记录(正如我们例子中的那种情况)。VFP自动对每个字段的oldval()值和curval()值进行比较,如果检测到任何不同,就会产生冲突。
l 用户输入了重复的主索引或候选索引值。
l 违背了某个字段或表的验证规则,或者不支持null的字段出现了null值。
l 某个触发(trigger)失败。
l 其它用户锁定了该记录。
l 其它用户删除了该记录。
当tableupdate()失败时,我们必须决定下一步做什么,而且,如果你的程序在编辑记录时允许用户点击"下一个"或"上一个"按钮,而这两个按钮中又没有调用tableupdate()的话,你必须得处理在自动保存时将会发生的错误。在这两种情况下,将程序指定到适当的位置就是错误陷阱处理程序。
Visual Foxpro中的多用户及数据缓冲问题(下)
--------------------------------------------------------------------------------
2000-10-6 17:07:00
在VFP中错误处理已经得到改进。以前处理错误陷阱的方法(你仍然可以在VFP中继续使用这些方法)是当错误发生时用on error命令来决定要执行的程序,典型的错误处理程序是查看error()和message()来确定发生了什么错误,然后采取相应的动作。
现在VFP提供了一种自动的错误处理机制:就是Error方法。如果定义了一个控件或表单中的Error方法,当错误发生时它就被自动执行。aerror()是VFP的一个新增函数,通过传递一个参数,该函数可以创建或更新一个含有以下元素的数组
元素 类型 描述
1 数字 错误号(与error()相同)
2 字符 错误信息(与message()相同)
3 字符 如果有一个错误信息参数(与sys(2018)相同),则返回之(例如:一个字段名),无,则返回.NULL.
4 数字或字符 发生错误的工作区。如果没有,则返回.NULL.
5 数字或字符 如果一个触发器失败,返回触发器号(插入为1,更新为2,删除为3),如果没有则返回.NULL.
6 数字或字符 .NULL.(应用于OLE和ODBC错误)
7 数字 .NULL.(应用于OLE错误)
例如:aerror(IaERROR)会创建或更新一个称为IaERROR的数组。
以下是VFP在将缓冲写入表时可能发生的一些错误:
错误号# 错误信息 说明
109 记录正由其它用户使用
1539 触发器失败 检测数组的第5个元素可以确定是哪个触发器失败了
1581 字段不接受空值(null) 检测数组的第3个元素可以确定是哪个字段引起的错误
1582 违反了字段的验证规则 检测数组的第3个元素可以确定是哪个字段引起的错误
1583 违反了记录的验证规则
1585 记录已被其它用户修改
1884 违反了索引的唯一性 检测数组的第3个元素可以确定是哪个索引标记引起的错误
对于以上这些错误,大部分可以直接处理:提示用户问题所在,然后让用户在"编辑"状态下改正错误或是取消操作。对于#1585错误(记录已被其它用户修改),有以下几种处理错误的方法:
l 可以提示当前用户有别人修改了该记录,然后用tablerevert()取消当前用户的编辑内容。我想多数人对这种方法会不高兴的。
l 可以用tableupdate(.F. , .T.)来强行更新记录,使当前用户的修改覆盖其它用户修改。这样做,当前用户自然是高兴的,但其它用户可能就要不满了。:(
l 复制一个相同的表单(在VFP中对同一表单创建多个实例是非常容易的),在上面显示出其它用户对该记录的修改,这样,当前用户就可以决定是保存还是不保存其它用户的修改,也就是说,可以用tableupdate(.F. , .T.)强行更新或是用tablerevert()来取消编辑。
有更好的方案来检测我们是否遇到了"真正的"冲突,所谓"真正的"冲突,是指两个用户在同一时间修改了同一个字段。如果他们修改的是同一记录的两个不同字段,我们可以只更新当前用户修改的字段,而保留另一个用户修改的字段,使之不受影响。例如,在一个订单处理系统中,一个用户修改了产品介绍,而同时另一个用户在对该产品下订单,正在输入数量,这两个修改相互独立,并没有冲突,这时我们不要一次更新整条记录,而是只更新自己修改的那个字段,如此一来,两个用户都会感到满意。
以下是实现这一想法的思路:
l 查找oldval()与curval()不同的字段,如果有,说明该字段已被其它用户编辑过,如果该字段的缓冲值与oldval()相同,说明当前用户并没有修改该字段。这种情况下,我们可以将curval()中的值先传给缓冲值,再更新,这样就可以避免缓冲值覆盖新值了。
l 查找缓冲值与oldval()不同的字段,这些字段是当前用户修改过的字段,如果oldval()又等于curval(),说明其它用户没有改动过该字段,这种情况下,我们可以放心地覆盖掉它。
l 如果我们找到的字段,其缓冲值与oldval()不同,但与curval()相同,这说明两个用户对同一字段做了相同的修改。这种情况看起来好象不大可能,其实是会发生的。比如有人发来了一个公司的地址变动信息,而恰恰有两个用户同时决定据此来更新记录中的公司地址。因为他们所做的修改内容是相同的,我们可以覆盖另一个即可。但是,如果是以同一个数量更新一个量值的话(比如两人同时下订单,且输入了相同的数量),你就不能简单地覆盖字段,应该将此看作为"真正的"冲突。
l 如果发现一个字段的缓冲值既不同于oldval()也不同于curval(),而且oldval()与curval()也各不相同,这说明两个用户都修改了同一个字段,且值不相同。这种情况才是我们遇到的真正的冲突,我们不得不决定怎样处理这一冲突。
在手工输入存货数量或帐目余额时,有一种可能就是其它用户输入的值对缓冲值会产生影响。比如,如果检测到oldval()是10, curval()是20,说明其它用户将数额已增加了10;如果现在缓冲值为5,说明当前用户要将数额在原基础(10)上减少5,因此新的缓冲值就应该是value+ oldval()- curval(),即等于15,应该用此数来更新字段。
对于日期型字段的冲突,就要考虑商业规则或按实际情况处理。例如,在"病人预约时间"程序中,有一个字段保存着病人下次预约医生的时间,如果该字段两个日期发生冲突的话,那么,靠近当前日期的那个日期很可能是正确的。当然,若是其中一个预约日期比当前日期还要早的话(已经过期),那就应该取靠后的那个日期了。
其它类型的字段,特别是字符型和备注型字段,通常情况下如果不询问用户以决定是覆盖其它用户的修改还是取消自己的修改的话,则冲突不好解决。只有当用户在屏幕上看到其它用户到底做了什么修改后,他才能做出正确地判断。
以下是解决冲突的一些代码(这些代码假设已检测到错误码为#1585,即记录已被其它用户修改过。)
* 检测每一个字段,看哪个发生了冲突。
llConflict = .F.
for lnI = 1 to fcount()
lcField = field(lnI)
llOtherUser = oldval(lcField) <> curval(lcField)
llThisUser = evaluate(lcField) <> oldval(lcField)
llSameChange = evaluate(lcField) == curval(lcField)
do case
* 其它用户编辑了该字段,而当前用户没编辑,所以直接用新值即可。
case llOtherUser and not llThisUser
replace (lcField) with curval(lcField)
* 其它用户没有编辑该字段,或者二者做了相同的修改,因此我们无需做任何处理。
case not llOtherUser or llSameChange
* 两个用户以不同的值修改了该字段。
otherwise
llConflict = .T.
endcase
next lnI
* 如果发生了冲突,处理之!
if llConflict
lnChoice = messagebox(''''Another user also changed this '''' + ;
''''record. Do you want to overwrite their changes (Yes), '''' + ;
''''not overwrite but see their changes (No), or cancel '''' + ;
''''your changes (Cancel)?'''', 3 + 16, ''''Problem Saving Record!'''')
do case
* 覆盖其它用户的修改。
case lnChoice = 6
= tableupdate(.F., .T.)
* 通过产生一个表单实例来查看其它用户的修改内容。
case lnChoice = 7
do form MYFORM name oName
* 取消当前用户的修改。
otherwise
= tablerevert()
上一页 [1] [2] [3] 下一页 [系统软件]Visual Studio 2005 Express Beta Products 下载链… [系统软件]曝光Visual Foxpro 9.0 最新秘密 [系统软件]FOXPRO 系统指标 [系统软件]Visual FoxPro9.0中扩展报表系统功能 [系统软件]Visual FoxPro:我是旁观者 [系统软件]Visual Studio 2005 Express Editions Beta 2 下载… [系统软件]ASP对FoxPro自由表(DBF文件)的操作 [系统软件]Boost库在XP+Visual C++.net中的安装 [系统软件]Visual Studio 2005 Express Edition 正式版下载地… [常用软件]Visual Foxpro通用报表打印程序
|