1555 ObjectPAL: Application code ideas
VERSIONS: All

SUBJECT: tCursors & User Defined Aliases

BY: STU BAILEY
Date: 16 May 2001

Much of the stuff that follows may be well known to everybody - in which case ignore it as old hat stuff from the other end of the world. But from some recent threads, maybe it has relevance.


TCursors - specifically, versus queries.

In my humble opinion, using TCursors to scan, with switchIndex and setRange, a number of tables to populate a local "Answer" table beats running queries in any multiuser app. And the bigger the tables, and the higher the concurrent user count, the bigger the performance payoff. Coding isn't fast or easy, but the rewards with an application running quick almost no matter the load on the network and server makes the whole thing more valuable to the Customer. And is a strong reason for the Customer to prefer a Paradox application over Access. A hint for the table to be populated - delete it (IF istable()) at the start of the code, create it from scratch (build indexes if need be), and start with a fresh table. Main reason is that empty(), particularly if indexes are involved, generates a lot of activity locally, doesn't recover disk space from the biggest record count for the table ever run, and can leave you from time to time with corrupted tables and/or out of date indexes.


User Defined Aliases.

The very idea of allowing users to set aliases via either BDE Config or through the Alias Manager makes me shiver. The worst thing they can do is get the path wrong, bring the app to a dead stop - and not have a clue why. Builds traffic for telephone support, builds user irritation, and makes developers/support people go bald. (Believe me, I am!) I always have my apps start from a script because that allows me to set session parameters (special date or number formats etc) and the :USER: and :DATA: aliases. Both are read from a table I routinely name CURRPATH in the same directory as the startup script. It contains three fields - USERPATH, DATAPATH and PARAM. A TCursor opens the table, and reads the data from the first two fields to set the alias paths for :USER: and :DATA: (I'll come to PARAM later). If the record is blank, the TCursor enters the path for the startup script, because that's where all the forms and reports will be anyway, and sets the :USER: alias to that path. If the DATA field is blank, the user gets a warning that they will need to set the path to their data from the next form that opens. The script then opens the :USER:mainform and waits until that form is closed, then exits Paradox.

The main form carries a button to take the user to a Set Data Directory form, which displays a table (CURRDATA) with three fields - DATANAME, PATH and DATATYPE. The user can enter any name they like for the data name - whatever takes their fancy. When they tab into the PATH field, FileBrowser opens and the User is prompted to indicate where their data is located. On departField, the :DATA: alias is set to the data path, and a TCursor updates the :USER:CURRPATH DATAPATH field. This ensures that on subsequent opening of the app, both aliases will be in place, and the database selected. I usually at the same time return a text string of the DATANAME field, and change a text label on the main form to remind them which data they are using.

Why the last bit? The trick is that the user can switch databases - simply by adding another record to the CURRDATA field, giving the database a name and a path. Then if they want to switch, all they have to do is move to the database name they want, and press ENTER to trigger a TCursor to update the :USER:CURRPATH DATAPATH field, and at the same time respecify the :DATA: alias.

The DATATYPE (in DATAPATH) and PARAM (in CURRPATH) allow an interesting opportunity. If (as in a couple of my customer's apps) there are different "processing rules" depending on the database in use, you can specify the :USER:DATAPATH DATATYPE field to accept only certain values (the user can determine which one) - and that value is written to the :USER:CURRPATH PARAM field. Then, in any form in which there is code which depends on a datatype specification to determine the processing rules to be applied, in the form's Open method have a TCursor open :USER:CURRPATH, and set a form var value to that in the PARAM field. As every component of the form has the form-level var within its scope, you can totally transform the way the data is displayed and/or processed - set sort orders on tableviews or MROs, set radio button defaults, and use IF, SWITCH - case and other loop controls to process data.

And the setting of aliases from data in the CURRPATH table ensures that when the user closes the app, it will restart in the database they were last using.

It is (almost - exception below) bulletproof. No permanent aliases to confuse issues, no user need to use BDEConfig, and the ability to switch databases at will. (Hint - don't put anything which is in the :DATA: directory on the main form - force the user to hit a button to go to the first form using :DATA: - give the platform a chance to reset its data sources!)

Exception is that either the user selects the wrong directory in the SET DATA DIRECTORY form, or they've moved the data (happens, particularly if drive or directory mappings get changed by IT people). Fixed by code prior to the setProjectAlias(":DATA": etc) to check that
a) the directory exists,
and
b) if so, that a key table is present

If either case is False, then give the user a MsgInfo that the data or directory is not correct, skip the :DATA:alias set/reset, and invoke fileBrowser for a new data address. Also add a similar check to the opening script, so if the data moves overnight, you can warn the user that they will have to reset their path to the data.

That makes it virtually bulletproof. Biggest test was a large project using volunteer, barely trained users in an election campaign. Not a single problem arose, although database switches were frequent, and new PCs added to the network as the campaign grew.

As I said at the top, you may already know all this stuff. But if you didn't, and think it may be useful, go for it!

To index