Subject Re: [IBO] Refresh question [LONG]
Author Helen Borrie
At 09:36 AM 10/12/2004 +1100, you wrote:

>Hi all,
>
>My knowledge of IBObjects is practically null, and I am having to develop
>applications which make use of it extensively. Thus, I am trying to use the
>OO concept of reuse as often as possible, programmatically setting up the
>different controls/components (IB_Connection, IB_transaction, IB_Query,
>IB_Grid, IB_Cursors, IB_Edit etc) so that the same control/component such as
>a IB_Query may be used by the application on different areas of the
>application.

This isn't exactly what OO is about. While it might be a useful way to
conserve resources under some conditions of usage, it doesn't enhance the
"object-orientedness" of the your application in any way.

OO is about re-using object code - polymorphism - capturing the
commonalities of classes and passing them down the chain - so that new
classes can be built on ancestor classes and make use (directly, or by
override, or by concretely implementing a virtual member) of the code that
already exists in the ancestors.


>I make sure that the application will never try to use the same
>component/control for two different queries simultaneously.

Great; but really you are only switching the contents of containers,
reusing existing object instances for divers datasets. If you have to
"queue up" the workflow of your applications to do this content-swapping,
you don't gain anything and potentially you lose.

On the other hand, by defining your own classes - which may be complex,
such as a reusable form or data module - and creating and destroying a
discrete instance each time you want one, you take advantage of the ability
to encapsulate and reuse application code at will, independently of what
the application or the user is already doing. This technique is simplified
by using polymorphic structures but it's not dependent on it. You can do
similar with VB which, while object-driven, is not an OO language.


>My main problem is that when I setup the controls/components
>programmatically, I on some occasions experience a different behaviour to
>when I setup the controls/components up using the "design-time database
>utilities". So I am obviously doing something wrong when setting up the
>controls/components programmatically.

Apart from the modal character of this "object re-use" approach (which is
not, IMO, *always* the wrong approach!), one of the traps is that, if you
are going to re-use an object instance, your code has to be meticulous
about cleaning out the previous occupant's stuff. The create/destroy
approach is superior because *every* instance is specific to its own unique
instantiation, the complexity is hidden and you don't add the overhead and
extra coding of explicit cleanup.


>Taking out the obvious (such as trying to set a parameter before adding the
>SQL to the IB_Query or IB_Cursor for example), is there any requirement to
>setup properties in a particular order? For example FieldsVisible before
>FieldsDisplayLabel? Or any other internal sequence that must be follows,
>which is not obvious?

Sure, because an already instantiated object will always be in some state.
Look at the source, or the component developer version of the helpfile, to
see the hidden properties and methods relating to testing dataset
state. The complex state-testing and branching involved are the concern of
the object; all the programmer ought to need is to call the published
methods and use the published properties of the object appropriately.


>One of the multiple problems I am currently having, which I manly
>experience when programmatically setting the properties of the
>controls/components is as follows.
>
>I am using a IB_Query to gather data and display it to users on a IB_Grid.
>Since the PK is Auto Generated, I have a trigger defined in the DB (which
>looks after the PK), and I (programmatically) set the GeneratorLinks
>property so that the application has knowledge of the value for the PK
>field.

Ironically, this problem doesn't relate to object-orientedness at all. It
just requires a properly written trigger (see Woody's posting on this
thread and the FAQ topic about it at the Firebird website). If a
GeneratorLink is set, it fires only when the object's Insert() method is
called. If you have already eliminated the problem of "double-firing" by
writing a safe Before Insert trigger for the table, then the double-firing
is happening because you are calling Insert() more than once.


>I also make use of the AfterAction event on the IB_UpdateBar. I check if the
>post button was click, if true, I call Refresh() on the IB_Query. However, I
>am obviously doing this the wrong way, since the next time the user adds a
>record, the value of the Generator has been increased, not by one, but by
>two.

This might also have to do with some kind of unintentional double action in
your application. Refresh() can't fire a generator, per se. However, if
the dataset is commanding a selectable stored procedure that itself fires
the generator, then you have to redesign it. SSPs should never change
database state (even though it is technically possible for them to do so) -
render unto Caesar that which is Caesar's. Create and call executable
procedures to change database state.

Another thing it's necessary to understand about generators is that you
must not rely on them to maintain number series with no gaps. Once a
generator generates a value, it is "gone", whether the transaction that
acquired it is committed or rolled back; and even while the transaction is
neither committed or rolled back. This is "as designed". It guarantees
that every generated number will be unique, regardless of the outcome of
any particular transaction.

If you need to maintain a series and you want to use a generator for it,
you can, but the operative word here is "maintain". You have to devise an
ecosystem of triggers and table structures explicitly for each
series. (There are some TI sheets on this topic on IBO's TechInfo page..)


>I have set the IB_Query property RequestLive to false, and the RefreshAction
>to raKeepDataPos.

These are client-side characteristics. They don't affect database state.

Just picking up an example you provided earlier "...such as trying to set a
parameter before adding the SQL to the IB_Query or IB_Cursor for
example...", this is one place where your policy of reusing instantiated
objects works against performance. If you have a task that requires to do
the same thing repeatedly throughout a user session, using the same data,
where only the search criteria will vary, then it makes no sense at all to
re-use the object.

If you set this object up in the IDE, that dataset need only be prepared
once in the whole session. Preparing is essential but it is also
costly. The prepared state of a dataset will survive commits, refreshes,
closes and opens if you don't do stuff that forces it to be unprepared.

Escape from the belief that instantiating objects in the IDE is in some way
"not OO". It is highly OO. It guarantees that, wherever an object
instance is re-used, the re-use will engage the minimum amount of resource.
It makes the exposed members of the class totally visible to you at design
time and, in the usual case where an application needs to do the same task
repeatedly, it saves enormously on performance, in terms of both code
cycles and network traffic, provided you use the objects as they were
designed to be used.

Obviously, continually churning the contents of a dataset object will force
this costly Unprepare/Prepare cycle to occur every time. Your
pail-scrubbing approach gains you nothing and loses you much. Apart from
introducing the risk of something failing due to dataset state at the time
your application needs the pail, you force a complete removal and
reconstruction of every structure inside the object.

On the other hand, if you keep the object instance persistent, by never
changing the SQL property and by using replaceable parameters for the
search criteria, then all you need to do for each different set of search
values is apply them to the already-prepared Params[] object. Dataset
state is totally predictable at each possible challenge point. The dataset
does exactly and only what it needs to do. And the code is minimal.

Helen