VFP数据库争用问题



我有一个非常大的VFP应用程序。我使用VFE框架。我的应用程序使用本机VFP数据库。应用程序已有20年历史,并且每天都在增长。10年来,我偶尔会发生车祸。它可以在同一天发生多次。它可能在一周或两周内不会发生,然后在一天内发生两次。我有20多个客户网站,每个网站同时有5到30个用户。网站越繁忙,崩溃的频率就越高。我一直认为这是由于VFP DBC争用问题,但从未得到证实。在过去的10多年里,我尝试过很多次,但都没能做到。我准备再试一次。我有一个DBC,大约有125张表。所有视图都在单独的"视图"中;视图";DBC,包含约1500个视图。

我收到一个1925年的错误";未知成员PARAMETERS";当崩溃发生时。我很乐意提供更多信息,但首先我想更好地了解我的视图数据库的锁定/争用问题。有人能帮我确切地理解这个问题是什么吗?

这是发生错误的代码。这种情况可能发生在其他几个地方,但这可能是最简单的例子:

  1. 我有多个应用程序。我有一个用作用户界面的应用程序。这就是我的用户每天都在使用的应用程序。我们正在讨论的错误从未出现在该应用程序中。我有一个应用程序,用来做";背景材料";。它没有用户界面,我不会这么说,但它是一个";COM对象";意味着它是OLE Public。我把这个节目叫做ITFCOM001。它是一个EXE。ITFCOM001.EXE是这些错误总是发生的地方。ITFCOM001基本上是一个";无尽循环";程序,用于执行从供应商导入、导出到供应商、生成报告数据等操作。我的用户界面应用程序和ITFCOM001都使用相同的表DBC和视图DBC
  2. 当ITFCOM001执行类似于供应商导出的操作时,会出现错误

ITFCOM001中发生错误的代码:

lojpayexportsbo=CREATEOBJECT("v_jpayexportsbizobj")
&& 11/13/2017 - add cd to try stop com001 crashes
IF NOT VARTYPE(lojpayexportsbo)='O'
lojpayexportsbo = NULL
RELEASE lojpayexportsbo
RETURN .f.
ENDIF
lojpayexportsbo.setparameter("vp_ctransfer_acct_id",tctransfer_acct_id)
lojpayexportsbo.requery()
lnretval=lojpayexportsbo.navigate("FIRST")
&& 11/30/2017 added lnretval= to above statement and then checking to ensure = FILE_OK below here
IF NOT lnretval>=-7
IF VARTYPE(lojpayexportsbo)="O"
lojpayexportsbo.release()
ENDIF
lojpayexportsbo = NULL
RELEASE lojpayexportsbo
RETURN .f.
ENDIF
lojpayexportsrec=lojpayexportsbo.getvalues()

在上面的代码中,setparameter是发生错误的地方。查看VFE类库错误实际上发生在FindViewParameter方法的第41行。

*==============================================================================
* Method:           FindViewParameter
* Purpose:          Finds the Parameter object associated with the passed 
*                   view parameter name.
* Author:           F1 Technologies
* Parameters:       tcParameterName, The name of the view parameter.
* Returns:          Object, The Parameter object.
* Modifications:
* 12/03/1999        Initialized loParameter to .NULL.
*==============================================================================
LPARAMETERS ;
tcParameter

LOCAL ;
loParameter, ;
loCursor, ;
lcCursor, ;
lcParameter
loParameter = .NULL.
WITH This   
IF "." $ tcParameter
lcParameter = TRIM(SUBSTR(tcParameter, AT(".", tcParameter) + 1))
lcCursor = LEFT(tcParameter,AT(".", tcParameter) - 1)
ELSE
lcParameter = tcParameter
lcCursor = ""
ENDIF
IF "!" $ lcCursor
lcCursor = SUBSTR(lcCursor,AT("!", lcCursor) + 1)
ENDIF
IF NOT EMPTY(lcCursor)
loCursor = .FindCursor(lcCursor)
ELSE
loCursor = .oCursor
ENDIF
IF VARTYPE(loCursor) = T_OBJECT
loParameter = loCursor.Parameters.Item(lcParameter)
ENDIF
* If the view parameter could not be found in the business object, check
* its parent.
IF VARTYPE(loParameter) <> T_OBJECT AND VARTYPE(.oParentBizObj) = T_OBJECT
loParameter = .oParentBizObj.FindViewParameter(tcParameter)
ENDIF
ENDWITH
RETURN loParameter
line 41 is "loParameter = loCursor.Parameters.Item(lcParameter)"

这是错误日志记录的值。这是调用堆栈:

0000KF7T0193
ITFCOM001APPLICATIONOBJECT.INIT
ITFCOM001APPLICATIONOBJECT.INIT_POST
ITFCOM001APPLICATIONOBJECT.EXPORTTOEDV
V_JPAYEXPORTSBIZOBJ.SETPARAMETER
V_JPAYEXPORTSBIZOBJ.FINDVIEWPARAMETER
V_JPAYEXPORTSBIZOBJ.ERROR

我可能还应该添加一件事:上面显示的代码在我所有的客户网站上每15分钟全天候执行一次。错误/崩溃只会在所有站点中非常间歇性地发生。

Stefan,我从未使用过ASSERTS,但我确实在VFP帮助中查找过它。使用ASSERTS没有帮助,因为这是COM对象而且它没有用户界面,所以即使调试对话框会显示(我不相信它会显示),ASSERTS也只能工作在我的开发环境中,而不是在实时系统中。而且,在我的开发环境中永远不会出现这个错误除非我还设置了一个非常复杂的测试,模拟用户在COM对象运行时进行数据输入这样做会非常复杂。但是,从概念上讲,你给了我一个我认为可行的想法。我可以修改FindViewParameter方法以将附加信息写入文本文件以获得更好的调试信息,或者也许我可以在记录错误时在ON ERROR例程中执行此操作。我今天会努力做到这一点,看看我能得到什么。

好的,我开始按照上面描述的去做。我决定修改我的ONERROR例程,以便在1925错误发生时写出额外的调试信息。我发现我的ON ERROR例程中已经有代码可以执行此操作,但该代码无法正常工作。我需要一些帮助来确定原因。这是我的ON ERROR例程中的代码:

LPARAMETERS     tnError,    tcMethod,   tnLine
LOCAL lcfacility,lcsubject,lctmpfilename,lntmpfilehndl
&& 01/06/2016 - added property and adding line here to set property true.
this.lerrorhasoccurred=.t.
&& will check in code where i suspect an untrapped error is occurring
&& 01/06/2016 - I should see one of these files everytime com001 errors.
lctmpfilename=ADDBS(ALLTRIM(this.capphomeonserver))+"TEMPITFCOM001_Log_errors_"+TTOC(DATETIME(),1)+".txt"
lntmpfilehndl=FCREATE(lctmpfilename)
FPUTS(lntmpfilehndl,"in oappcom001.error. errno="+ALLTRIM(STR(tnError,6,0)))
FFLUSH(lntmpfilehndl,.t.)
FCLOSE(lntmpfilehndl)
DO CASE
&& Ignore error caused by issuing a CLEAR ALL in the init of this class.
CASE tnError = 1951 AND UPPER(ALLTRIM(tcMethod)) == "INIT"
RETURN

&& Ignore errors causes by issuing SET DATASESSION TO an invalid data session.
CASE tnError = 1540
RETURN
&& Ignore errors opening the project as a table. GetProjectData will use the
&& last copy of the meta data table if the project can't be opened. This allows 
&& VFE to run in one instance of VFP and a second VFP instance to be used to
&& run the project.
CASE tnError = 1705 AND UPPER(ALLTRIM(tcMethod)) == "GETPROJECTDATA"
RETURN    

&& Handle any other errors
OTHERWISE
&& send me a text
&& I took all the code out that sends me a text via Twilio to make this more readable.
DoDefault(tnError, tcMethod, tnLine)
ENDCASE
RETURN

关于上述代码:当任何VFE应用程序启动时,VFE总是生成错误1951。一直都是这样。所以,每当我启动这个应用程序时,都会出现错误1951,这个ON error例程被执行,我在临时文件夹中看到文本文件,它包含文本";在oappcom001.error.erno=1951中";。该错误将被忽略,程序将运行。

当出现错误1925时,此ON error例程未被执行。我怎么知道?未创建文本文件。这告诉我,在我的应用程序中,或者在我正在使用的某个类库中,正在更改ON ERROR,并使其可能使用默认的VFP错误处理例程,而不是这个ON ERROR例程。我需要先找到它。

因此,我试图了解执行哪个ON ERROR例程可能会发生什么变化,很快我就意识到我已经做了很多次了,而且一直被证明是一条死胡同。仅仅阅读VFE如何处理错误的帮助主题是如此复杂,以至于我需要一个月的时间来理解帮助主题。我不能那样做。

这需要回到我最初的问题。我没有很好地提出最初的问题。让我再试一次:

经过多年的处理和研究,我得出的结论是,这在某种程度上与VFP数据库争用问题有关,即创建视图时数据库会被锁定。尽管这听起来可能是错误的,但我已经放弃了确定1925年错误发生的原因,而是更喜欢一种变通方法。所以回到基础:

  1. 我的具有用户界面的应用程序名为INMATETTRUSTUND。它被编译为EXE。它有一个包含125个表的表数据库。它有一个";视图";拥有1200个视图的数据库
  2. 我的应用程序名为ITFCOM001,它正在获取1925个错误。它是一个编译为EXE的COM对象。它没有显示器,也没有用户界面。它只是在后台运行。它使用相同的表和视图数据库

我怀疑发生了类似以下的事情,这就是导致1925个错误的原因:

INMATETTRUSTFUND中的一个用户正在执行打开视图的操作,以便在生成视图时锁定视图数据库。在同一时刻,ITFCOM001确定是时候向供应商进行导出了,并执行创建v_jpayexportsbizobj的代码。这样做将访问视图数据库。VFP会尝试锁定视图数据库,但它已经被锁定,因为INMATETTRUSTUND将其锁定以打开其他视图。一切都出了问题(该语句中有很多未知内容),ITFCOM001生成1925错误。

我相信,但在我决定之前,我希望更好地了解VFP数据库争用问题,如果我给ITFCOM001它自己的独立视图数据库,它与INMATETRUSTUND使用的视图数据库具有相同的视图,但它不需要所有1200个视图,而只需要ITFCOM01使用的大约100个视图,这个问题可以简单地消失,这意味着VFP数据库争用问题将消失,因为这两个应用程序永远不会使用相同的视图数据库。

再次,我真的很想完全理解";VFP数据库争用";问题,然后再尝试使用独立视图数据库的可能解决方案。

谢谢,约翰·

我想留言,但我显然没有足够的声誉。。。我知道这个帖子已经发布了6个月了,但OP已经为此工作了10年,如果我能帮忙,为什么不呢?:-)

为Stefan&Tamar,当我查看错误消息和代码时,我得出结论,在错误点,loCursor是一个Object,不为null,但它没有一个名为Parameters的成员。这向我表明,This.FindCursor(lcCursor)或This.oCursor要么返回了一个意外的对象类型,要么可能是一个未正确实例化的对象。。。?

正如Stefan所建议的,额外的日志记录应该会有所帮助。我建议添加一个TRY。。CATCH块记录错误、调用堆栈、loCursor的值/type/class和tcParameter的值(这应该允许您识别loCursor是由This.FindCursor(lcCursor)还是由This.oCursor返回的)。我怀疑根本原因在于This.FindCursor()或This.oCuror默认值。我认为你应该能够实现一个TRY。。。CATCH而不更改全局ON ERROR代码,并将原始错误(记录后)抛出/提升到ON ERROR(在CATCH内或TRY CATCH块后),以保持ON ERROR层次结构中调用的任何代码像以前一样运行。

如果经过考虑,您认为FindViewParameter()从This.FindCursor(lcCursor)或This.oCursor中获取一个不是有效游标对象的值是有效的,那么您可以通过替换来抑制这些错误

如果VARTYPE(loCursor)=T_OBJECT

带有:

如果TYPE("loCursor.Parameters")=T_OBJECT

甚至可能:

IF TYPE("loCursor.Parameters")#;U〃。和VARTYPE(loCursor.Parameters)=T_OBJECT

(如果您向TYPE()传递一个无效引用,它不会抛出错误,但毫无帮助地不会告诉您对象引用是否为null,所以请将两者结合起来)这可能已经足够了,但最终可能会出现.item()不是loCursor.Parameters成员的错误。

在这种情况下,您可以使用TRY catch块捕获并抑制错误。根据评论,FindViewParameter()在1999年的某些情况下被修改为返回null……如果在1999年它优先于",他们一定认为这很重要;千年虫;准备工作!哎呀,我的年龄越来越大了!:-)

希望这能有所帮助。祝你好运

相关内容

  • 没有找到相关文章

最新更新