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.
|