GFX Collection DataSession-Handling Sample (TMM)

 
Visual FoxPro 9.0 SP2
This page is an addition to the VFP SP2 CHM, covering a class that implements the interface of the SP2 Visual FoxPro Reporting System's FFC FX subsystem.
Back to TMM Index
GeneralAbout this class
GFXExample illustrates both proper and improper datasession handling during a report run.

The help file includes some general recommendations for data-session handling during a REPORT FORM command in the Data Sessions Associated with Object-Assisted Report Runs section of the Understanding Visual FoxPro Object-Assisted Reporting topic, because every such command potentially involves three or more datasessions:
  • the datasession containing data for the report (ReportListener.CurrentDataSession)
  • the datasession in which the REPORT FORM command executed (ReportListener.CommandClauses.StartDataSession), which is not the same as the datasession containing data for the report when the FRX has a private data session
  • the ReportListener's private work datasession (ReportListener.FRXDataSession)
  • the datasession in which the ReportListener was instantiated (_ReportListener.listenerDataSession)
When the addition of helper objects through the SP2 FX Reporting subsystem is considered, there are potentially many more datasessions involved, because each helper object may have been instantiated in a different session.

It is not a good practice to create FX subsystem objects randomly in datasession that may belong to forms invoking a report.  Once they are added to the FXs or GFXs collection, they may "live" far longer than the first form that needed them, resulting in dangling references and what is commonly known as "stuck" datasessions in your application environment.  Additionally, when a VFP object's  methods are called, the object ordinarily pulls the action back to the datasession in which it was "born" -- which may not be the session containing the data on which the object needs to act.

The FFC classes generally try to create helper objects in the default datasession (datasession ID 0),  because this practice eliminates the "stuck" sessions that result from objects created in transient datasessions. This practice allows the collection members to exist safely after the forms are destroyed. But the approach does not resolve all possible datasession combinations and issues, if a helper object switches datasessions during the report run to handle different types of data.

GFXExample's ApplyFX method shows a technique the object can use to remove itself from FXListener's collection at the conclusion of a report run. This technique is critical to safe use of FX and GFX objects that do not closely monitor their use of datasessions. Alternatively, a class can carefully save and restore datasessions during every action it takes; GFXExample also includes commented-out code that illustrates this approach.

Note Note
There is no real significance to this class having been marked a GFX rather than an FX by name; its behavior would be the same in either collection.  In the sample code below, we add it to the FXs collection.

PEMs commentsProperties and Methods

FXMemberDataScript adds only one public member to the required FX interface:

Properties and methods Description

showDataSessionIssue Property

Toggles demonstration of proper datasession handling in this example GFX class.

Default: .T.

ExampleExamples

The following code is the full text of the ApplyFX method for this class. 

Notice that, when GFXExample releases itself from the FXListener's collection using the RemoveCollectionMember method of the FXListener class, it uses THIS.Name.  By default, the AddCollectionMember method has generated a unique name for every collection member you have added. The default behavior ensures that you can always safely address collection members uniquely even if they are not Singletons.  Singleton collection members can usually be safely addressed by their .Name member property, even if it is not generated, and they can also be addressed by their .Class property when you use the FXListener.RemoveCollectionMember method.

LPARAMETERS m.toListener, m.tcMethodToken,;
            m.tP1, m.tP2, m.tP3, m.tP4, m.tP5, m.tP6,;
            m.tP7, m.tP8, m.tP9, m.tP10, m.tP11, m.tP12
            

IF m.tcMethodToken == "BEFOREREPORT" AND ;
   THIS.showDataSessionIssue AND ;
   (m.toListener.CurrentDataSession = m.toListener.CommandClauses.StartDataSession)
   MESSAGEBOX("This report does not use a private data session," + CHR(13) + ;
              "so you won't see the problem.")                          
ENDIF              
IF m.tcMethodToken == "BEFOREBAND" 
   SET DATASESSION TO (m.toListener.CurrentDataSession)
   m.toListener.doStatus("working here... ")
   IF THIS.showDataSessionIssue 
      * no switch back here.
*!*	   ELSE
*!*	      SET DATASESSION TO (m.toListener.ListenerDataSession)
   ENDIF      
ENDIF               

IF (NOT THIS.showDataSessionIssue) AND ;
   m.tcMethodToken == "AFTERREPORT"
   * if the following is not included,
   * a "stuck" datasession results unless
   * some additional object later in 
   * the collection did the switch back
   m.toListener.removeCollectionMember(THIS.Name,.T.)
   RELEASE THIS
ENDIF

The following code shows how you might add this class to an FXListener collection to see correct and incorrect datasession behavior.

LOCAL ox
DO (_REPORTOUTPUT) WITH 1, ox
? ox.addCollectionMember("gfxexample","_reportlistener")
REPORT FORM  c:\temp\customers OBJECT ox
SET && no stuck session, because CUSTOMERS.FRX does not have a private datasession
CLEAR ALL
MODIFY REPORT c:\temp\customers.FRX && add private datasession to the report
LOCAL ox
DO (_REPORTOUTPUT) WITH 1, ox
? ox.addCollectionMember("gfxexample","_reportlistener")
REPORT FORM  customers OBJECT ox
SET && you see an "Unknown" session in the Data Session window
CLEAR ALL
LOCAL ox
DO (_REPORTOUTPUT) WITH 1, ox
? ox.addCollectionMember("gfxexample","_reportlistener")
oy = ox.FXs[1]
oy.showDataSessionIssue = .F.
REPORT FORM  customers OBJECT ox
SET && no stuck session, because gfxExample releases itself
            

See AlsoSee Also