Extending the cConnection Class
The connect interface can be extended at the cConnection object level or class level. The cApplication object can also be extended to change how it works with managed connections.
Using cConnection Interfaces in an Object
The default configuration and behavior of a connection object can be changed via properties, methods, and events. These can all be applied directly to your cConnection object. Some possible extensions are shown below.
Object oApplication is a cApplication
Set psProduct to "DataFlex Examples"
Object oConnection is a cConnection
// You can create a different encryption package and use it here
Use YourLoginEncryption.pkg
// You can create a different database login dialog and use it here
Use YourDatabaseLoginDialog.dg
// If false, the Auto-connect (register and login) does not occur and you
// must do this yourself. The cApplication object attempts to auto connect
// when a workspace is opened via DoOpenWorkspace.
// You could also customize connect behavior by augmenting AutoConnect
Set pbAutoConnect to True|False
// If false, the database login dialog is never invoked.
// If True a database login dialog must be provided
Set pbAllowLoginDialog to True|False
// If false, there is no encryption of passwords.
// If true, an encryption object must be provided
Set pbEncryptPassword to True|False
// Additional properties can also be set.
Set pbUseConnectionIni to True|False
Set pbSkipDuplicateLogin to True|False
Set pbLoginOnOpen to True|False
Set psConnectionIni to "SomeName"
Set pbCmdLineIniAllowed to True|False
Set psConnectionIdCmdLine to "Something"
// This is called by the cApplication object after a workspace is
// opened via DoOpenWorkspace.
// If pbAutoConnect is true, it does a register and login all
// This could be customized to do whatever you want. Note this is not an
// event. It contains code and it can be directly called by the developer
Procedure AutoConnect
End_Procedure
// Events you may code but are less likely to do so
Procedure OnDriverRegistered
End_Procedure
Procedure OnAddConnection
End_Procedure
Procedure OnReadCredentials
End_Procedure
Procedure OnWriteCredentials
End_Procedure
End_Object
// Called after a workspace is successfully opened and any auto-connect
// has occurred. This is only called if DoOpenWorkspace is used to open a
// workspace (which is the standard way to open a workspace).
Procedure OnWorkspaceOpened
// this is a new event
End_Procedure
End_Object
Customizing the cConnection and cApplication Classes
The class interface and code structure were designed to provide a trade-off between ease of use and developer flexibility. We expect that developers will wish to extend these classes and objects to fit their own needs. The main new pieces added to support managed connections are a global cConnection object, a global login dialog, and a global login encryption object. These are singleton objects (i.e., there is only one in an application). There are some dependencies between these objects:
- The
cConnectionrequires acApplicationobject. - The
cApplicationneeds to know thecConnectionobject’s handle. - The
cConnectionobject needs to know the handles of the encryption object and the login dialog object.
These are determined by using global object handles: ghoApplication, ghoConnection, ghoLoginEncryption, and ghoLoginConnectDialog. Global handles were used because these are global objects and they provide the greatest flexibility when dealing with object dependencies (object names don’t matter and the order of object definition matters less).
The way this code is organized makes it easy to customize your connection handling at the object level. Because these components are all separate, they are also easy to subclass. For example, you could customize your cConnection class to reflect your preferences as follows:
Use cConnection.pkg
Use LoginEncryption01.pkg // this is my customized encryption object
Class cConnection01 is a cConnection
Procedure Construct_Object
Forward Send Construct_Object
Set pbLoginDialogRequired to False
Set pbLoginOnOpen to False
Set pbCmdLineIniAllowed to False
End_Procedure
Procedure AutoConnect
Integer iErr
Send RegisterAllConnections
// Only login to connection ID1 at start-up
Get LoginConnectionId "ID1" to iErr
If iErr Begin
Send UserError "Could not login to primary Connection. Contact IT"
Abort
End
End_Procedure
Procedure OnDriverRegistered String sDriverId Integer iDriver Handle hoDriver
// turn off cache
Set_Attribute DF_DRIVER_USE_CACHE of iDriver to False
End_Procedure
End_Class
This class could then be used in your application source file as follows:
Object oApplication is a cApplication01
Set psProduct to "DataFlex Examples"
Object oConnection is a cConnection01
End_Object
End_Object
You could take this a step further and decide that you want to completely hide the connection object inside of your cApplication subclass. You would configure your application and connection objects for your specific needs and lock them down in a reusable class. Here is an example of this:
Class cApplication01 is a cApplication
Procedure Construct_Object
Forward Send Construct_Object
Set psProduct to "DataFlex Examples"
Object oConnection is a cConnection01
End_Object
End_Procedure
End_Class
This class could then be used in your application "src" file as follows:
Object oApplication is a cApplication01
End_Object
Creating a hidden cConnection object allows you to centralize your connection and login preferences in a class. It has the disadvantage that it is harder to customize at the cConnection level. The trade-off choice is yours.
Customizing the Connection Process
There are various layers of customization available when working with managed connections. At the highest level, you read your connections from a connections INI file, register them all, and log in to all connections. If any of this fails, your application is aborted. This all occurs before any table is opened. At the next level down, you could control the RegisterAllConnections and LoginAll processes so you can handle errors yourself. You could also choose to obtain your connection string information from a different source or embed this information directly within your application. This example bypasses the auto-connect logic and creates a code-based connection. This is not particularly useful, but it shows the flexibility of what can be done:
Object oApplication is a cApplication
Set psCompany to "Data Access Worldwide"
Object oConnection is a cConnection
Set pbAutoConnect to False // don’t do an auto-connect
Set pbUseConnectionIni to False // don’t use a connections INI file
Set pbLoginDialogRequired to False // no login dialog (it’s trusted)
Set pbLoginEncryption to False // no encryption (it’s trusted)
End_Object
End_Object
// Loading connections with custom code-based logic
Procedure LoadCustom
Integer iErr
Boolean bOk
Get AddConnection of ghoConnection "MSSQLDRV" "ID1" "SERVER=.\SQLExpress;DATABASE=OrderEntry" "" "" True "" to bOk
Get LoginConnectionId of ghoConnection "ID1" to iErr
If (iErr) Begin
Send UserError "Login Attempt Failed"
Abort
End
End_Procedure
Send LoadCustom
Just as an example, the connection logic here is placed outside of the cConnection and cApplication objects in a procedure called LoadCustom. The code in LoadCustom could have just as easily been placed in the cConnection AutoConnect method or placed inside the cApplication OnWorkspaceOpened event. What this demonstrates is that the registration and login process can be fully controlled – it does not need to use information from a connections INI file and it can be executed at any time.
Previous Topic
Recommendations and Requirements