Welcome to Foxite.COM Community Weblog Sign in | Join | Help

Opening Tables

One of the most frequently written snippets of code, in almost any application, looks something like this:

*** Save Current work area
lnSelect = SELECT()
*** Select Required Area
IF ! USED( <Alias> )
  USE <table_name> IN 0 AGAIN ALIAS <Alias>
ENDIF
SELECT <New Work Area>

*** Do Something There

<commands>


*** Return to original work area
SELECT (lnSelect)


Now, admittedly, this is not really very difficult, but it or some variant is repeated many times in an application. We really should be able to do better than this now that we have all the power of Object Orientation behind us and indeed we can. The SelAlias class defines an object that accepts the alias name of a table as a parameter and switches to that table's work area. If the table is not open it will open the table for us and if the table cannot be found, it will prompt us to locate it. More importantly it will 'remember' that it opened the table and when the object is released it will close the table and restore the work area that was current when the object was instantiated. The class also provides support for an additional parameter which can be used to specify a physical table name when it is necessary to open a table with an alias other than the real name of the table.

Note that this class has no exposed properties or methods and does all of its work in its Init and Destroy methods. By creating an object based on this class, and scoping it as LOCAL, we need never write code like that shown above again. The class is intended to be used whenever you need to open a table ‘on the fly’ and so typically I use it by creating a local variable to use as the reference to the object. The consequence is that as soon as the method which created the reference ends the object is released and the table is closed (if it was not open already) and the original work area is re-selected.

The class is based on a relation - it is a small and lightweight base class that is quick to instantiate but, unlike the empty base class still has the native Init() and Destroy() methods.

I have used this code extensively ‘as is’, but it could easily be extended to handle other conditions and scenarios – for example it will not work with views as it stands because it assumes that a physical file exists – but this is easily fixed if you need it to handle that scenario too. Anyway, here is the code - feel free to modify and improve it:

********************************************************************
*** Name.....: SELALIAS.PRG
*** Author...: Andy Kramek & Marcia Akins
*** Date.....: 06/10/2006
*** Notice...: Copyright (c) 2006 Tightline Computers, Inc
*** Compiler.: Visual FoxPro 09.00.0000.3504 for Windows
*** Function.: Class to Select a specific Work area and restore it
*** .........: Use as a local object to change work area.
*** .........: When released will restore the previous work area
*** .........: Accepts two parameters, the first is the Alias and is mandatory
*** .........: The second is the name of the table.  If not passed, assumed to be the
*** .........: same as the Alias you are using.
*** .........: loSel = CREATEOBJECT( 'xSelAlias', <Alias> [, |<File Name> ] )
********************************************************************
 
********************************************************************
DEFINE CLASS xSelAlias AS relation
******************************************************************
  PROTECTED nOldArea
  nOldArea = 0
  PROTECTED lWasOpen
  lWasOpen = .T.
  PROTECTED cAlias
  cAlias = ''
 
  ****************************************************************
  *** Init: Native initialization method
  ****************************************************************
  PROCEDURE Init( tcAlias, tcTable )
    LOCAL llRetVal
    *** No Alias Passed - Bail Out
    IF ! VARTYPE( tcAlias ) = "C" OR EMPTY( tcAlias )
      ASSERT .F. MESSAGE "Must Pass an Alias Name to Work Area Selector"
      RETURN .F.
    ENDIF
    tcAlias = UPPER( ALLTRIM( tcAlias ) )
    IF VARTYPE( tcTable ) # "C" OR EMPTY( tcTable )
      tcTable = tcAlias
    ELSE
      tcTable = UPPER( ALLTRIM( tcTable ) )
    ENDIF
    *** If already in correct work area - do nothing
    IF UPPER( ALLTRIM( ALIAS() ) ) == tcAlias
      RETURN .F.
    ELSE
      *** Does the table exist?
      tcTable = FORCEEXT( tcTable, 'dbf' )
      IF NOT FILE( tcTable )
        *** Try and find it....
        tcTable = LOCFILE( JUSTFNAME( tcTable ), 'dbf', "Cannot Find" )
        IF EMPTY( tcTable )
          *** Nothing more we can do
          RETURN .F.
        ELSE
          *** May have got the wrong name - set the alias to the table name
          tcAlias = JUSTSTEM( tcTable )
        ENDIF
      ENDIF
    ENDIF
 
    *** If Specified Alias not open - Open it
    IF ! USED( tcAlias )
      USE ( tcTable ) AGAIN IN 0 ALIAS ( tcAlias ) SHARED
      *** And Check!
      llRetVal = USED( tcAlias )
      *** If Forced Open, Note the fact
      IF llRetVal
        This.lWasOpen = .F.
      ENDIF
    ELSE
      llRetVal = .T.
    ENDIF
    *** If OK, save current work area and
    *** Now Move to the specified Work Area
    IF llRetVal
      This.nOldArea = SELECT()
      SELECT ( tcAlias )
      This.cAlias = tcAlias
    ENDIF
    *** Return Status
    RETURN llRetVal
  ENDPROC
 
  ****************************************************************
  *** Destroy: Native Destroy Method
  ****************************************************************
  PROCEDURE Destroy
    WITH This
      *** If table opened by this object, close it
      IF NOT .lWasOpen
        USE IN ( This.cAlias )
      ENDIF
      *** Restore Previous work area
      IF NOT EMPTY( .nOldArea )
        SELECT ( .nOldArea )
      ENDIF
    ENDWITH
  ENDPROC
 
ENDDEFINE

Published Saturday, June 10, 2006 1:19 PM by andykr

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Opening Tables

Tuesday, June 13, 2006 8:20 PM by Fermin

I think it would be interesting adding another optional parameter, order.

Yes, but then what about all of the other things? (Exclusive, Shared, NoUpdate etc etc) Where do you  stop?

# re: Opening Tables

Friday, June 16, 2006 6:26 PM by stuartd

"One of the most frequently written snippets of code, in almost any application, looks something like this..."

That depends a lot on the application architecture. Preserving the current workarea isn't necessary unless you are using XBase code as SQL doesn't care what workarea you are in: most of the xbase functions are now workarea-agnostic anyway. If the app is designed not to depend on what workarea is selected, then there is rarely the need to set it specifically.

Things are only "workarea-agnostic" if you explicitly reference the required alias (and if the table is actually open under that alias) so while you are correct that it is a matter of design - that is only part of the story. It is also highly version-dependent. It is really only in the most recent versions of VFP (VFP8 and 9) that the native SQL engine was enhanced to the point where using SQL in place of xBase code was a realistic proposition. Similarly it is only in the later versions that the dependence on work area was removed from the extended set of commands/functions.

For those (and I suspect that they constitute the majority of VFP developers world-wide) who are still working in VFP 6.0, or even 7.0, the opportunities to do things this way, or to use SQL extensively, are much more limited.

What do you think?

(required) 
required 
(required)