 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
David Keith Guest
|
Posted: Thu Nov 27, 2003 7:11 pm Post subject: datasource |
|
|
I'm using Asta 3.1 and am editing some apps written by developers who
don't like using datamodules. I do like using datamodules so that I can
maintain a clear separation between presentation code and data code.
One problem I have run into is that AstaClientDatasets have a property
called updatespending, which is applicable when you use editmode
CachedUpdates.
This setting is darn useful for setting buttons and data-bound controls
to enabled/disabled based on whether or not the attached datasurce fires
the statechange event.
The problem comes in when I move datasource/clientdataset objects to the
datamodule. I can't figure out a way to use the statechange event in a
datasource on a datamodule to enable/disable controls on a form. I've
tried passing in buttons as a pointer, but of course this didn't work.
Any suggestions?
Thanks.
David Keith
|
|
| Back to top |
|
 |
John McCarten Guest
|
Posted: Sun Nov 30, 2003 6:04 am Post subject: Re: datasource |
|
|
"David Keith" wrote
| Quote: | The problem comes in when I move datasource/clientdataset objects to the
datamodule. I can't figure out a way to use the statechange event in a
datasource on a datamodule to enable/disable controls on a form. I've
tried passing in buttons as a pointer, but of course this didn't work.
|
I tend toward doing it like this
I have a datamodule (say dmInvoice) and on it I have a datasource component (say dsInvoice) pointing
to a database table as you will. What I do is wrap methods (and/or properties) of the component(s)
as methods of dmInvoice. Pseudo-services as it where. Eg.
procedure TInvoiceDataStateChange(whatever, whatever) of object;
TdmInvoice
private
FOnDataStateChange: TInvoiceDataStateChange;
public
property OnDataStateChange: TInvoiceDataStateChange read FOn ... write FOn ...
.... then
procedure TdmInvoice.dsInvoiceStateChange(whatever, whatever);
begin
if Assigned(FOnDataStateChange) then
FOnDataStateChange(whatever, whatever);
end;
.... then on the form that presents the invoice to the user (say fmDisplayInvoice)
TfmDisplayInvoice
private
procedure DataStateChange(whatever, whatever); // TInvoiceDataStateChange
....
constructor TfmDisplayInvoice.Create (...)
begin
...
dmInvoice.OnDataStateChange := DataStateChange;
...
end;
procedure TfmDisplayInvoice.DataStateChange(whatever, whatever);
begin
Button1.Enabled := whatever;
end;
I tend toward taking this approach with properties also. All in all, I find that it helps to
develop datamodules in this way (as dataproviders) to expose a regular interface when developing
presentation forms, without the forms themselves becoming overly concerned with where the data
originates. Eg.
TdmInvoice
private
function GetLineItemsCount: Integer;
public
property LineItemsCount: Integer read GetLineItemsCount;
function TdmInvoice.GetLineItemsCount: Integer;
begin
Result := dsInvoiceLineItems.RecordCount;
end;
I find this approach especially useful when interacting with more than one kind of database or
server.
j.
|
|
| Back to top |
|
 |
David Keith Guest
|
Posted: Sun Nov 30, 2003 9:34 pm Post subject: Re: datasource |
|
|
John McCarten wrote:
| Quote: | "David Keith" wrote
The problem comes in when I move datasource/clientdataset objects to the
datamodule. I can't figure out a way to use the statechange event in a
datasource on a datamodule to enable/disable controls on a form. I've
tried passing in buttons as a pointer, but of course this didn't work.
I tend toward doing it like this
I have a datamodule (say dmInvoice) and on it I have a datasource component (say dsInvoice) pointing
to a database table as you will. What I do is wrap methods (and/or properties) of the component(s)
as methods of dmInvoice. Pseudo-services as it where. Eg.
procedure TInvoiceDataStateChange(whatever, whatever) of object;
TdmInvoice
private
FOnDataStateChange: TInvoiceDataStateChange;
public
property OnDataStateChange: TInvoiceDataStateChange read FOn ... write FOn ...
... then
procedure TdmInvoice.dsInvoiceStateChange(whatever, whatever);
begin
if Assigned(FOnDataStateChange) then
FOnDataStateChange(whatever, whatever);
end;
... then on the form that presents the invoice to the user (say fmDisplayInvoice)
TfmDisplayInvoice
private
procedure DataStateChange(whatever, whatever); // TInvoiceDataStateChange
...
constructor TfmDisplayInvoice.Create (...)
begin
...
dmInvoice.OnDataStateChange := DataStateChange;
...
end;
procedure TfmDisplayInvoice.DataStateChange(whatever, whatever);
begin
Button1.Enabled := whatever;
end;
I tend toward taking this approach with properties also. All in all, I find that it helps to
develop datamodules in this way (as dataproviders) to expose a regular interface when developing
presentation forms, without the forms themselves becoming overly concerned with where the data
originates. Eg.
TdmInvoice
private
function GetLineItemsCount: Integer;
public
property LineItemsCount: Integer read GetLineItemsCount;
function TdmInvoice.GetLineItemsCount: Integer;
begin
Result := dsInvoiceLineItems.RecordCount;
end;
I find this approach especially useful when interacting with more than one kind of database or
server.
j.
Thanks, John. It'll take me a while to digest this. I really appreciate it. |
David Keith
|
|
| Back to top |
|
 |
John McCarten Guest
|
Posted: Mon Dec 01, 2003 1:00 am Post subject: Re: datasource |
|
|
"David Keith" wrote
| Quote: | It'll take me a while to digest this.
|
Just by way of further explanation. (I whacked out the last post in a bit of a hurry).
The idea is to treat your datamodules as business objects (to borrow a middleware term) rather than
treat them solely as transport or connection facilities as is often done.
In the Invoice example, what I was attempting to show was a method of doing this in a relatively
simple way. (Relative to OPFs that is. Discussions of which can be found in the delphi.oodesign
newsgroup. Well worth a look and I recommend that you do this).
Anyway, back to business objects the easy way.
We have this datamodule called dmInvoice and on it we may have a single TQuery called qyItems
connected to a Paradox table. The idea is to expose the methods and properties of the TQuery as
"direct services" of the business object dmInvoice, and have the presentation layer "talk" to
dmInvoice rather that to dmInvoice.qyItems. The main reasons for doing this are:
1) To make the presentation layer (the forms) of your applications as database-independent as
possible. When you do a lot of data-centric applications as most of us do, then over time the
benefits of this become increasingly obvious. Reuse et al.
2) All of your database connectivity is contained in one place (obviously).
3) Database-dependent components contained within your business object dmInvoice can be swapped out,
without unduly interfering with or breaking your presentation layer. This will inevitably happen if
your forms are talking directly to dmInvoice.qyItems and you change to another type of query
component.
4) Business rules can be contained within the business object dmInvoice as described here. (Some may
argue that another layer should inserted here, and I will return to this later. But it should be
remember we are not neccessarily building a fully-fledged OPF here, only a reasonably
straight-forward implementation of a tiered client/server application within the constraints of
out-of-the-box components). In your particular case what we are looking at is: Presentation layer
(forms) -> client-side business objects (datamodules) -> remote Asta server -> Database.
Now all of this assumes that you are not using data-bound controls. If you are, then this approach
can cause you more headaches than what its worth, should you try to code it all yourself. However
there is a simple solution.
If we remember that one of the main purposes of this approach is to use client-side business objects
that provide consistent interfaces to your presentation layer regardless of the database/transport
at the back, then the easiest way to use data-bounds controls in this scenario is to deploy an
intermediary TDataset component as a "service" of your business object. Kim Madsen's TkbmMemTable
(www.components4developers.com) for example. Eg.
TdmInvoice
Header: TkbmMemTable;
Items: TkbmMemTable;
qyHeader: TQuery;
qyItems: TQuery;
Connect your data controls to Header and/or Items as required. And from your presentation layer
invoke requests of TkbmMemTable. Within the body of dmInvoice have TkbmTable pass the requests onto
TQuery. Do this and you can swap out your TQuery components for others at any time without breaking
your presentation layer.
Returning to the matter of the business rules layer mentioned earlier. On the occasions when I have
gone down the data-bound route, I have arranged my modules like so:
Presentation layer (forms)
- data-bound controls
Business objects (datamodules)
- intermediary dataset component
- business rules
Connectivity module (datamodule)
- database and transport specific components
Presentation <-> Business <-> Connect
The method of exposing internal component events as "services" of the business objects (dmInvoice)
contained in my last post still holds.
j.
|
|
| Back to top |
|
 |
David Keith Guest
|
Posted: Mon Dec 01, 2003 7:24 am Post subject: Re: datasource |
|
|
John -
Hope you don't mind my top posting. This is an interesting discussion.
I've used kbmmemtable quite a bit, especially in financial apps. Because
it's thread-safe it makes a greater 'pointer-capable' component to pass
in to threads and so forth. I use it in my current job whenever I need a
more capable in-memory dataset than TAstaDataset. Not to diminish from
Steve's work in any way, but Steve is focused less on the advanced
capabilities of a TDataset-compatible in memory dataset than Kim Madsen is.
Another interesting tidbit that I learned from working with Steve's
products is that each instantiation of a datamodule - and it's contained
data objects - is a threaded db session. Just more reason to use
datamodules in the first place. )
My most basic need here is to know on the form when the datasource is
in [dsInsert,dsEdit] so that I can enable/disable the save/cancel
buttons. When closing forms I also iterate through the
TAstaClientDatasets checking for UpdatesPending to prompt the user to
save/cancel any unsaved changes.
On the one hand I do find it annoying to have a zillion data components
on datamodules, and have considered a more programmatic approach. On the
other hand it helps to be able to maintain large, complex apps by having
the visual references that the visual components provide.
I'm moving in the direction that you outline below, learning to use
datamodules, for example, in place of normal units for my globals unit.
This way I can host object specific functions and procedures using the
global datamodule as a 'substitutionary' owner. I've found I can reuse
events for different types of objects by using the datamodule as the
owner, and querying the sender parameter for RTTI. It has simply become
logical to me. I'd never heard it enunciated using the Business
Objects/Services language as you describe it. It sounds like a lot of work.
Also we depend heavily on Oracle & MS SQL Server stored procedures. The
vast majority of our client-side data components are TAstaClientDatasets
used strictly for hosting stored procedure calls and parameters. Our
Asta & AstaIO servers act as 'pass-through's', translating AstaParamList
data into Oracle/SQL Server structured procedure calls. This helps us by
simplifying our client-side data work, and minimizes our distribution &
maintenance issues.
I'm always walking the tightrope between a good object oriented design
and abstraction to the extreme. I tend to view reuse, readability,
combined with separation of function, as the most productivity enhancing
approaches.
I'll give this some thought.
Thanks.
John McCarten wrote:
| Quote: | "David Keith" wrote
It'll take me a while to digest this.
Just by way of further explanation. (I whacked out the last post in a bit of a hurry).
The idea is to treat your datamodules as business objects (to borrow a middleware term) rather than
treat them solely as transport or connection facilities as is often done.
In the Invoice example, what I was attempting to show was a method of doing this in a relatively
simple way. (Relative to OPFs that is. Discussions of which can be found in the delphi.oodesign
newsgroup. Well worth a look and I recommend that you do this).
Anyway, back to business objects the easy way.
We have this datamodule called dmInvoice and on it we may have a single TQuery called qyItems
connected to a Paradox table. The idea is to expose the methods and properties of the TQuery as
"direct services" of the business object dmInvoice, and have the presentation layer "talk" to
dmInvoice rather that to dmInvoice.qyItems. The main reasons for doing this are:
1) To make the presentation layer (the forms) of your applications as database-independent as
possible. When you do a lot of data-centric applications as most of us do, then over time the
benefits of this become increasingly obvious. Reuse et al.
2) All of your database connectivity is contained in one place (obviously).
3) Database-dependent components contained within your business object dmInvoice can be swapped out,
without unduly interfering with or breaking your presentation layer. This will inevitably happen if
your forms are talking directly to dmInvoice.qyItems and you change to another type of query
component.
4) Business rules can be contained within the business object dmInvoice as described here. (Some may
argue that another layer should inserted here, and I will return to this later. But it should be
remember we are not neccessarily building a fully-fledged OPF here, only a reasonably
straight-forward implementation of a tiered client/server application within the constraints of
out-of-the-box components). In your particular case what we are looking at is: Presentation layer
(forms) -> client-side business objects (datamodules) -> remote Asta server -> Database.
Now all of this assumes that you are not using data-bound controls. If you are, then this approach
can cause you more headaches than what its worth, should you try to code it all yourself. However
there is a simple solution.
If we remember that one of the main purposes of this approach is to use client-side business objects
that provide consistent interfaces to your presentation layer regardless of the database/transport
at the back, then the easiest way to use data-bounds controls in this scenario is to deploy an
intermediary TDataset component as a "service" of your business object. Kim Madsen's TkbmMemTable
(www.components4developers.com) for example. Eg.
TdmInvoice
Header: TkbmMemTable;
Items: TkbmMemTable;
qyHeader: TQuery;
qyItems: TQuery;
Connect your data controls to Header and/or Items as required. And from your presentation layer
invoke requests of TkbmMemTable. Within the body of dmInvoice have TkbmTable pass the requests onto
TQuery. Do this and you can swap out your TQuery components for others at any time without breaking
your presentation layer.
Returning to the matter of the business rules layer mentioned earlier. On the occasions when I have
gone down the data-bound route, I have arranged my modules like so:
Presentation layer (forms)
- data-bound controls
Business objects (datamodules)
- intermediary dataset component
- business rules
Connectivity module (datamodule)
- database and transport specific components
Presentation <-> Business <-> Connect
The method of exposing internal component events as "services" of the business objects (dmInvoice)
contained in my last post still holds.
j.
|
|
|
| Back to top |
|
 |
John McCarten Guest
|
Posted: Tue Dec 02, 2003 6:38 am Post subject: Re: datasource |
|
|
David wrote
| Quote: | On the one hand I do find it annoying to have a zillion data components
on datamodules, and have considered a more programmatic approach. On the
other hand it helps to be able to maintain large, complex apps by having
the visual references that the visual components provide.
|
About the zillion components on one datamodule. Gets difficult doesn't it when you take it to far
. Look to separate them out into different datamodules related by task. When you do this you
begin to get a visual representation of what your client-side business objects might look like. In
my view, a visually represented business object is as valid (and can often be more productive) than
one implemented purely in code. I have found datamodules to be eminently suitable for this purpose.
I also much prefer pictures to words, which is why I use an IDE :)
| Quote: | ... This helps us by simplifying our client-side data work, and minimizes our
distribution & > maintenance issues
|
There is a lot to be said for putting as much as you can on the server, but in a number of instances
this is not always possible and you need a reasonably well-rounded client-side model in these
situations.
For example; one of our clients has a relationship management system for which we built a front-end
with Delphi. The application is not only deployed within the company at various sites around the
world, but also with a number of their suppliers and customers. Not surprisingly, the suppliers and
customers have their own backends and even within the company there are differing backend systems at
various sites. We are somewhat restricted in what we can and cannot do with them.
Our brief was/is to provide a user interface to present and manage company information (regardless
of source) in a consistent way without putting undue pressure on the company's existing resources
and those of their suppliers and customers. So, we had to develop a model that would be doing a fair
bit on the client-side, while also allowing us to respond in good order to any server-side changes
initiated by them.
| Quote: | I'm moving in the direction that you outline below, learning to use
datamodules, ... I'd never heard it enunciated using the Business
Objects/Services language as you describe it. ...
|
BO/S speak is just one of those natural evolvements of language. Business objects and services are
not the sole preserve of any particular person or group of persons, be they business managers,
accountants, salespeople or software developers. An invoice is an invoice is an invoice. Sure, an
invoice can be physically represented in different ways and even thought about differently from
varying perspectives, but fundamentally it is the same thing no matter how it is viewed or
constructed.
I have found that when I describe what I am doing in BO/S speak to business people (the ones who
write the cheques and use the software) it makes all our lives a lot easier. They can relate more
readily to what is being discussed. Start talking about components, datasets, properties and methods
and they soon lose interest.
I have found that I talk a lot of BO/S speak these days, even in forums such as this. Which I don't
think is necessarily a bad thing. I also find that it helps move things along when in meetings with
software developers of other persuasions - specially when the meetings are convened by paying
clients :)
j.
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|