一、问题描述
有个绑定数据表的子窗体,改了其中一个字段(该字段绑定到一个文本框)的值之后,希望同一记录的另外一个字段的值做相应变化,另外也引起其他表和控件的一系列变化。
这所有相关变化我事先写了个函数,因为还有好几处别的地方要用它。
我在该文本框的afterupdate事件过程里调用了这个函数,之后在子窗体中移动到下一条记录,就会有写入冲突。原因我也明白,因为子窗体上改动了这个记录,还没彻底改完,我又在所调用的函数里通过ADO又改动了同一记录,而子窗体的记录集一般缺省是DAO的。这样就造成两种途径同时访问同一记录。
二、2种未遂的解决方法
我试了下面两种方式处理这个写入冲突,都不太好使。
第一种思路是来自http://www.accessoft.com/bbs/showtopic.asp?ID=700
1.用VBA断开与窗体的绑定
application.echo =false'此行解决操作员看不见画面的变化
me.recordsource =""
2.再执行表中记录的操作
3.用VBA实现与窗体的绑定
me.recordsource ="SQL语句"
application.echo =True
这个思路的缺点在于,执行之后,我的子窗体不再能正确地显示主窗体的内容,好像失去了和主窗体的联系。
第二个思路是,不在afterupdate事件过程里调用我的函数,而在afterupdate事件之后的事件过程里调用我的函数。可是这个也行不通,因为我无法确定,afterupdate事件之后必然发生的有什么事件。因为这是个子窗体,当我改变了子窗体的数据,然后点击主窗体里的控件后,子窗体控件的exit lostfocus事件均不发生。
三、权宜之计
最后,我只好把我调用的那个函数加了个布尔参数,如果是来自子窗体的调用,则不执行那段改动字段值的代码,而把这段代码直接放到文本框的afterupdate事件过程里做。
虽然问题算凑合解决了,可还是挺不爽的。有没有什么更好的解决写入冲突的办法啊?比如我有没有可能在afterupdate事件过程里强制更新数据,然后之后我再用ADO改同一记录就不会产生写入冲突了?还有我上面失败的两个思路,是不是真的就不行还是我的具体操作有问题?
四、后记
这种冲突的本质还有另一方面,就是事件机制。就是事件只能轮流执行,在事件A过程里激发另一个事件B的话,事件B处理完之后,事件A才算处理完。所以,在上面的权宜之计后,我又进一步完善了解决冲突的方法。就是我会在子窗体的控件的afterupdate事件过程里触发事件B;在这个事件B的过程里做改变子窗体绑定记录以为的变化,然后触发事件C、让子窗体做一些自己绑定记录的变化;然后再触发事件D,让父窗体更新显示(从而也更改子窗体的当前记录);最后再回到afterupdate事件过程的末尾。上述思路的伪代码如下:
……
RaiseEvent B
……
End Sub
B()
'改动非绑定数据
……
'子窗体改动绑定记录
RaiseEvent C
'父窗体更新显示
RaiseEvent D
……
End Sub
五、后后记
我终于找到了一个解决这个问题的办法,就是在beforeupdate取消这个操作,具体如下:
Dim strFv() As String
Cancel = True
Me . strName . Undo '不undo就不能对dirty赋值
Me . Dirty = False
ReDim strFv( 1)
strFv( 0) = "bytMultiGua=3"
strFv( 1) = "strName=" & Me . strName
Call ChangeDetailInTableDAO( "action" , Me . lngId , strFv)
Erase strFv
End Sub
像上面这样写就不会有写入冲突了。即使我那个函数ChangeDetailInTable用的ADO,也没事。我理解这3个貌似重复的语句,起了不同的作用,分别在和不同的对象或系统打招呼。
Me . strName . Undo '这一句是和数据引擎打招呼,取消某种锁定状态,不undo就不能对dirty赋值
Me . Dirty = False '这一句是和窗体打招呼,这样窗体移到下一条记录的时候,不会更新
我发现undo这个方法很奇怪。貌似它并不真的undo,而只是解锁。就是解掉数据库被更改不允许操作的状态,而控件的值,其实还是新值,并没有像在word里那样被undo回老值。
还是感谢Tiger_Zhao,他提醒我:一个程序只用一个连接,最主要不是避免“写入冲突”,而是保证数据的一致性。数据对象操作数据库是可能存在缓存的,如果你用多个连接(特别是 ADO 和 DAO 异种连接),难免会发生A连接做了更新、而B连接读取的数据没有反映最新的更新。