Databases - Delphi/KADao Runtime Path
Applications
- Are developed on one (or several) systems
- Formally tested on another system
- Used for actual (production) work on yet another
In each case, the path to the databases might be different
(unless you always require the same drive and path on every system).
In general, I prefer portable code that is able to determine
the appropriate path at run time.
In general, this is no big deal - just read an *.ini file and set the parameters.
However, a problem occurs when
KADaoDatabase1.Connected is True when the program compiles.
To develop applications, it is useful to set
KADaoDatabase1.Connected to True
(so you can see the available tables, fields, and example data).
However, if that is done, and you try to run the application
on another system, you will get a "file not found" error when the application starts.
Background
| Architectural Goals
| Datamodules
| Reading the ini File
|
Background
I have worked places where the database location was hardcoded
in the application - in order to run the code against a test database,
you had to manually tweak the code.
As a result, you could never test the deliverable code
until it was running against production data.
(Of course, the code was delivered at least once without making the
required last minute change.)
There are several ways to configure an application at runtime
- Use ODBC aliases and have them configured differently for each system
- One of the reasons I use KADao is so that I don't have to use ODBC aliases
- Store the *.mdb paths in the registry
- Before DotNet, Microsoft suggested storing all
application configuration information
in the registry, now they say that ini files are better.
I try very hard to write code that does not use the registry.
- Use an *.ini file
- This is what I prefer ... it is the topic of this page
Architectural Goals
These are the main architectural goals
- A single exe file can run against different databases (*.mdb files)
based only on the directory that contains the exe file
- It does not matter how many (or which) databases are used
in a specific exe file, all the programs in a given directory
read the exact same ini file and,
therefore, use the same databases
- Development and test can occur on the same machine and still use different
databases (ODBC aliases do not allow this)
- To deliver a new, tested production system, all you have to do
is to copy the tested files from one directory to another.
The only difference is a single ini file that specifies
which databases (*.mdb files) should be used.
Datamodules
In my design, each database (*.mdb file) is associated with one or more
datamodules.
- There is no advantage to accessing multiple databases from a single
datamodule because there is no way to construct a query
that uses more than one database.
- Code to validate data and populate combo boxes is added to each
datamodule so that it can be easily reused
- Each application (*.exe file) uses one or more datamodules as required.
- Datamodules can be reused in both *.exe files and web servers.
Reading the ini File
Because forms reference objects (fields) defined in datamodules,
the datamodules must be created first in the Delphi project (*.dpr) files
followed by the form.
When it is created, the datamodule is automatically connected
to the database specified in TKADaoDatabase.Database.
Since this occurs before the form is created,
the ini file must be read elsewhere.
While it is possible to read the ini file from each datamodule,
it makes more sense to create a special datamodule just to read the
common ini file.
- Common code does not need to be repeated
- The name of the common ini file only has to be set once
Of course, this datamodule must be created before any others.
To control the datamodule and form creation order, either
- Directly edit the project (*.dpr) file, or
- Via Project / Options... / Forms,
drag the Auto-create forms to change their order
Because of how TKADaoDatabase is implemented, this architecture
- Requires that the ini file be read
- As each datamodule is created, the ini file data is used to set
the database path
- The main form is created
Technique
TKADaoDatabase has an undocumented OnBeforeConnect event.
While it can not be used to abort the connection
(what I originally wanted),
it can be used to read the correct value from the ini file.
When each datamodule is created,
the TKADaoDatabase Set_Active method is called before
any other event I can find.
Set_Active eventually calls OnBeforeConnect.
After that returns, Set_Active checks for the existence of the file.
Eventually, DataModuleCreate is called (way too late to have an effect).
For this to work
- A special datamodule is created just to process the ini file
(SLP_Jet_Location_DM in the example below)
- SLP_Jet_Location_DM must be created in the *.dpr file before it is called
- Set OnBeforeConnect to code similar to the example
- The ReadString parameters must match those in the ini file
procedure TDataModule2.KADaoDatabase1BeforeConnect(
Database: TKADaoDatabase);
var
c : string;
begin
// this sets the correct database from an ini file when the program starts
// but allows another path to be used at design time
if csLoading in ComponentState then begin
c := SLP_Jet_Location.mcINI_File1.ReadString('Databases', 'Samples',
Database.Database);
Database.Database := c ;
end;
end;
Sample *.ini File
This is a typical Jet_Location.ini file with 4 databases (.mdb files)
- the "active" section
specifies which set of database files are actually used.
This file contains several similar sections
Either - add a leading character to comment out sections
or - change the order and the last section is the one used
[Databases]
Samples = G:\SLP_DB\Jet databases\Samples.mdb
Barcode = G:\SLP_DB\Jet databases\BarcodeGenerator.mdb
SOPs = G:\SLP_DB\Jet databases\Procedures.mdb
Users = G:\SLP_DB\Jet databases\General.mdb
[Databases]
Samples = C:\SLP_DB\Jet databases\Samples.mdb
Barcode = C:\SLP_DB\Jet databases\BarcodeGenerator.mdb
SOPs = C:\SLP_DB\Jet databases\Procedures.mdb
Users = C:\SLP_DB\Jet databases\General.mdb
[xDatabases]
Samples = C:\test\SLP_DB\Samples.mdb
Barcode = C:\test\SLP_DB\BarcodeGenerator.mdb
SOPs = C:\test\SLP_DB\Procedures.mdb
Users = C:\test\SLP_DB\General.mdb
This file contains 3 similar sections
- The middle section is the one actually used - its data
replaces the parameters from the first section.
- The name of the last section is intentionally mispelled so that it will
be ignored.
- The first section is used for development
- The second section is used for production (different drive letter)
- The last section is used for formal test
- The version delivered to production sould have only one section
Notes
May be able to include several ini files so that the application's
data can also be read and the database location ini file
(must be a separate file) can be set to another location
by a command line parameter.
I tried this, but it did not work
because DataModuleCreate is called after
the values stored in the DFM file are set.
procedure TSOP_DataModule.DataModuleCreate(Sender: TObject);
begin
KADaoDatabase1.Connected := false ;
end;
Related Commands
SmartOpen controls looking for missing databases in the current directory
and is not useful in this case.
Author: Robert Clemenzi -
clemenzi@cpcug.org