Efficiently access either of two Delphi datamodules with same dataobject names

411 Views Asked by At

I have an ugly situation where I need two datamodules (TDMA, TDMB) in a Delphi app. Each datamodule has the same data object names (queries, tables, etc.) but from a different component set (TZQuery, TADOQuery). I need this because I want to support multiple databases but not all databases are supported by my component suite. Which datamodule I need to access is determined by the DBFlag boolean variable. Besides having separate nearly identical code segments for each data access, is there some more efficient way?

If I could set a global datamodule variable like DMG to either DMA or DMB based on DBFlag then my code could reference DMG instead of DMA or DMB. That would be ideal and require very little code modifications but impossible as far as I know.

2

There are 2 best solutions below

6
AlexSC On BEST ANSWER

My suggestion is to drop building your DataModules based on specific datasets. Build them using only TClientDataSet and write all your code or link all your DataSources to these datasets. Then create other DataModules to hold your specific datasets and use your selection method to choose which one to respond as a data provider for the CDS instances. The idea of using an interface to do that is indeed a good one.

This approach will remove all you duplicate code and you will separate business logic (the code that handles the data inside the CDSs) from persistence (the code that transfers data rows from and to the data server).

0
MartynA On

It's perfectly possible to have a global variable or, better, a function or singleton class or perhaps best of all, an interface, which returns a reference to an abstraction of what dmA and dmB have in common depending on a boolean flag. Exactly how to do it, though, would benefit from some careful thought, most of which you will need to do yourself because only you know the details of your project and its requirements.

However, there are a few potential issues with it that I think are likely to lead you towards an implementation like one now-deleted comment suggested, that does away with datamodules (or hides them away) and uses custom DB objects or interfaces instead to provide consumer access from forms, reports, etc.

One big issue is to do with the way object visibility works in Delphi + its IDE.

Consider a project which has a unit MyForm1u which is to be a consumer of your DB objects. and units dmAu and dmBu which contain DB components that you've added via the IDE designer, that may have the same names in both units but can be of different instance types.

Now, MyForm1u can certainly use dmAu and dmBu, but that has the problem that the DB components in dmA and dmB are necessarily of published visibility (because that's what needs to be the case for them to be streamable and IDE-designable). So, although you could have a function which returns (an instance of) dmA or dmB, if MyForm1u Uses dmAu and dmBu, there's nothing enforce encapsulation of them so that access to them goes only via that function.

What you could do is to define a common ancestor datamodule, call it dmCA in unit dmCAu and then descend dmA and dmB from it - this would be a bit fiddly to do after the fact because if dmA and dmB already exist, you would need to hand-edit their DFM files to adjust component ancestry if you wanted to have some components on dmCA instead. But starting anew, you could easily create a new dmCA in your project, containing any DB components in common between dmA and dmB which are of the same instance type, and then descend dmA and dmB from it in the IDE.

This would give you a project structure in which myFormu1 doesn't Use dmAu or dmBu directly nor, some would say, dmCA. A better approach might be to have it Use none of them but rather a unit X which contains a function returning some class, or better interface, which in turn has a group of functions which return references to the components of dmA and dmB that they have in common in term of name (actually the important thing is their function within the data model of the datamodule) and ancestral type, e.g.

function MyDataSet1 : TDataSet;

so that it can return the AdoQuery1 of dmA or the SqlQuery1 of dmB, depending on your boolean flag.

Provided your consumer unit(s) Use only unit X and not dmAu, dmBu or dmCAu, that will enforce the encapsulation of their contents.

This class- or interface-based approach would preclude "wiring up" consumer objects like TmyForm1 to DB objects using the usual point 'n click approach courtesy of the IDE's Object Inspector, but these days many would say that that would be no bad thing.