Site menuContactSitemapProduktenInfoWie zijn wijWelkomStartDownload



TBcpRecordCount

Marco Wobben 26 feb 2002
[download] [reageer]

Recordcount voor Desktop en DBMS
De migratie van een desktop database naar een DBMS gaat niet altijd over rozen. Een klein hobbeltje dat dit artikel neemt betreft de wensen van eindgebruikers om het aantal records in beeld te krijgen terwijl zij bladeren.

Lijsten
Lijsten op het scherm zijn voor desktop databases veelal zonder problemen te tonen doordat de bestanden direct door de applicatie benaderd worden. Bij een DBMS gaat dit niet op, het gaat hier vaak om een netwerk verbinding en bestanden zijn niet direct te benaderen. De records moeten over het netwerk worden getransporteerd, pas als ze allen naar de applicatie zijn overgebracht kan het aantal records worden bepaald.
 
Een BDE dataset haalt alle records uit de database op, op het moment dat de RecordCount property wordt uitgelezen. Voor een netwerk verbinding is dit niet altijd wenselijk.
 
Het component dat in dit artikel wordt beschreven toont de record positie en het totaal aantal records dat is opgehaald van de database. Optioneel is om het component alle records op te laten halen om een exacte bepaling te verkrijgen.

Positie en totaal
Om in te stellen wat we laten zien is er een enumerated type gedefinieerd. Deze kan de volgende waarden bevatten:

type
TBcpRecordDisplay = (rdCount, rdPos, rdCountPos, rdPosCount);

Het toont alleen het totaal aantal records, de positie van het huidige record óf het totaal met de positie óf de positie met het totaal.

Het component
Het component TBcpRecordCount is een eenvoudige descendant van TCustomLabel. Hiervan is de belangrijkste property de DataSource die een verbinding met een dataset mogelijk maakt. Daarnaast wordt ingesteld hoe de weergave is met behulp van de RecordDisplay en de Seperator.

  published
property
DataSource: TDataSource read GetDataSource
write SetDataSource;
property RecordDisplay: TBcpRecordDisplay
read FRecordDisplay write SetRecordDisplay;
property Seperator: string read FSeperator
write SetSeperator;
property AllowFetchAll: Boolean read FAllowFetchAll
write SetAllowFetch;

Stel AllowFetchAll op True om alle records uit de dataset op te halen voor een exacte aantal bepaling. Stel het in op False om dit niet te doen en genoegen te nemen met het aantal reeds binnengehaalde records.
 
Natuurlijk zijn er meer properties dan bovengenoemde maar deze maken het mogelijk om het als een TLabel in te stellen en niet met de primaire functionaliteit die het component biedt.

Count zonder FetchAll
Het tellen van het aantal records in de dataset is niet mogelijk zonder een aangepaste SQL query. Dit component registreert het scrollen in de dataset en houdt aan de hand hiervan bij hoeveel records er uit de database zijn opgehaald. Tevens wordt hiermee bijgehouden op welk record we ons begeven in de dataset. Zolang er geen Eof is aangetroffen wordt de telling bijgewerkt. Zodra alle records zijn opgehaald ziet de weergave van het Label er als volgt uit: "13/356". Indien het nog niet alle records heeft is de weergave bijvoorbeeld: "13/13+" of "26/250+".

Data Aware
Om het component data-aware te maken moeten we gebruik maken van een TDatalink. Zonder datalinks is er geen eenvoudige communicatie mogelijk tussen ons component en de dataset. Echter de Datalink bevat geen enkele events en dit is wel wat we nodig hebben. Om deze reden maken we een descendant van de DataLink, TBcpDataLink, en overriden de nodige methods en definieren een event.
Van de dataset willen we in elk geval de DataSetScrolled afvangen. Deze geeft verschuivingen weer in de dataset en wordt gebruikt om de record positie bij te houden. Tevens kijken we hier of de Eof wordt aangetroffen.

  TBcpDataLink = class(TDataLink)
private
...
protected
procedure
ActiveChanged; override;
procedure RecordChanged(Field: TField); override;
procedure LayoutChanged; override;
procedure DataSetScrolled(Distance: integer); override;
procedure FetchAll;
public
constructor
Create; virtual;
property RecordPos: integer read GetRecordPos;
property MaxPos: integer read GetMaxPos;
property FetchedAll: Boolean read FFetchedAll;
property OnChanged: TNotifyEvent read FOnChanged write
SetOnChanged;
end;

Zoals te zien is hier de logica ondergebracht en niet in de TBcpRecordCount zodat we in de toekomst deze DataLink kunnen hergebruiken. Afgezien van het RecordCount uitlezen (de fetch all) is alles hier in onder gebracht.
Het event OnChanged wordt afgevuurd zodra er een dataset wijziging is gedetecteerd en afgehandeld.
Bij het uitlezen van de MaxPos of de RecordPos resulteert in het aanroepen van deze method:

function TBcpDataLink.GetRecordPos: integer;
begin
if
Bof then
FRecordPos := 1
else
if
Eof then
begin
FRecordPos := FMaxPos;
FFetchedAll := True;
end;
Result := FRecordPos;
end;

Als we een Bof detecteren weten we dat de record positie 1 is.
Als we een Eof detecteren dan weten we dat we alles binnen hebben gehaald en zetten we de vlag die weergeeft dat dit gebeurt is.
De MaxPos die RecordCount simuleert zodra we nog geen Eof hebben wordt bijgewerkt indien nodig.

TBcpRecordCount

Uiteindelijk is het component relatief eenvoudig. Het heeft een enkele method die wordt aangeroepen op het moment dat onze DataLink een OnChanged afvuurt. Hierin wordt de caption geupdate aan de hand van de instellingen.
Als de count moet worden afgebeeld bekijken we eerst of we een fetchall mogen uitvoeren, zoja dan zorgen we dat alle records worden ingelezen. Zoniet, dan moeten we de DataLink property MaxPos gebruiken.

procedure TBcpRecordCount.DoUpdate;
var
Count, Pos: string;
begin
if
(FLink.Active) then
begin
{ prepare helper strings }
if (FRecordDisplay in [rdCount, rdCountPos, rdPosCount])
then begin
if
FAllowFetchAll then
begin
TBcpDataLink(FLink).FetchAll;
Count := Format('%d', [FLink.RecordCount]);
end
else
begin
Count := Format('%d', [TBcpDataLink(FLink).MaxPos]);
if not(TBcpDataLink(FLink).FetchedAll) then
Count := Count + '+';
end;
end;
if (FRecordDisplay in [rdPos, rdCountPos, rdPosCount]) then
Pos := Format('%d', [TBcpDataLink(FLink).RecordPos]);
{ update the caption }
case FRecordDisplay of
rdCount: Caption := Count;
rdPos: Caption := Pos;
rdCountPos: Caption := Format('%s%s%s',[Count,FSeperator,
Pos]);
rdPosCount: Caption := Format('%s%s%s',[Pos,FSeperator,
Count]);
end;
end
else
Caption := '...';
end;

De FetchAll method zorgt er voor dat de dataset het eind bereikt en zet daarna herstelt daarna de cursor positie. (Voor BDE datasets is het uitlezen van RecordCount voldoende, echter meer en meer alternatieven zijn er die dit BDE gedrag niet onderschrijven en dus geen FetchAll uitvoeren).

Hieronder de implementatie om elke dataset het aantal records te laten ophalen:

procedure TBcpDataLink.FetchAll;
var
B: TBookmark;
begin
if
(Active) and (not FetchedAll) then
begin
FFetchedAll := True;
B := DataSet.GetBookmark;
DataSet.DisableControls;
try
DataSet.Last;
finally
DataSet.GotoBookmark(B);
DataSet.FreeBookmark(B);
DataSet.EnableControls;
end;
end;
end;

Conclusie

In dit artikel hebben we een component gemaakt dat illustreert hoe veeleisende desktop database gebruikers tevreden kunnen worden gesteld zonder afbreuk te doen aan de DBMS werking en bijbehorende principes.
De code illustreert een eenvoudige opzet van een data-aware component dat zowel geschikt is voor desktop databases als een DBMS zonder dat de werking ingewikkeld wordt.


Copyright © 2002 Bommeljé Crompvoets en partners

Het is toegestaan dit artikel in zijn geheel te kopiëren en te verspreiden, mits de tekst woordelijk in tact blijft en deze notitie bevat. Het is toegestaan om deze tekst te citeren, of te wijzigen, mits de oorspronkelijke auteur en houder van het copyright vermeld worden.
You may republish this paper verbatim, including this notation. You may update, correct, or expand the material, provided that you include a notation stating the original author and copyright holder.








[English] [welkom] [wie zijn wij] [info] [diensten] [producten] [download] [contact] [verwijzingen]