[Delphi + ADO] Problemen met nieuwe records bij autonummerin

Pagina: 1
Acties:
  • 175 views sinds 30-01-2008
  • Reageer

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
In een Delphi-applicatie gebruik ik ADO om een Access 2000 database te benaderen. Dat werkt naar behoren. Een aantal tabellen heeft een autonummering-veld als primary ID. In een grid control worden twee tabellen met een autonummeringsveld gebruikt om een master-detailrelatie te tonen. Dat gaat goed, totdat gebruikers records gaan toevoegen.

Dan gebeurt het volgende:
  1. Een gebruiker voegt een master record toe (in tabel A). Aan deze record wordt in het grid geen ID toegekend, dat wordt immers door Access gedaan (tabel A heeft autonummering) bij een update.
  2. De gebruiker voegt vervolgens een detail record toe (in tabel B) aan de zojuist toegevoegde master record. Ook hieraan wordt geen ID toegekend, want dat moet Access weer door met zijn autonummering.
  3. Bij het toevoegen van de detailrecord geeft Access een foutmelding dat hij de detail record refereert aan een onbekende ID in tabel A. Dat is ook logisch, want mijn applicatie heeft geen idee welke ID Access heeft gegeven aan de zojuist in tabel A toegevoegde record. En de in tabel B toe te voegen detail record moet aan deze ID refereren om de master-detail-relatie tussen beide records tot stand te brengen.
Mijn vraag: hoe los ik dit probleem op?

De randvoorwaarden:
  • Na iedere recordmutatie alle master en detail records opnieuw opvragen in de database (een refesh dus) is hopeloos traag en bovendien verlies ik daarmee allerlei gegevens zoals de op dat moment geselecteerde record. Een refresh is daarom geen optie.
  • Het problem duikt ook op in een vergelijkbare situatie: als de gebruiker eerst een (master) record in tabel A toevoegt en vóór een refresh van die tabel wijzigingen probeert aan te brengen in de nieuwe record, gaat het ook mis. Het is dan voor Access namelijk niet duidelijk welke record het moet updaten, want de te wijzigen record heeft nog geen ID toegewezen gekregen door de autonummering van Access.
Een mogelijke oplossing is de autonummering niet door Access te laten doen, maar door mijn applicatie. Maar wat voor type veld kies ik daar dan voor? Het moet eigenlijk niet Access-specifiek zijn (al weet ik dat een autonummeringsveld dat wél is). Een random GUID is misschien een oplossing, wat denken jullie daarvan?

Een goede grap mag vrienden kosten.


Verwijderd

Ik zou nooit editen in een grid. Een grid is imo een manier om gegevens weer te geven, niet om te editten of toe te voegen.

Als je gebruik zou maken van een aparte form om je nieuwe record in te voeren ben je volgens mij al van het probleem af.

[ Voor 34% gewijzigd door Verwijderd op 11-11-2003 16:49 ]


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
Verwijderd schreef op 11 november 2003 @ 16:46:
Ik zou nooit editen in een grid. Een grid is imo een manier om gegevens weer te geven, niet om te editten of toe te voegen.

Als je gebruik zou maken van een aparte form om je nieuwe record in te voeren ben je volgens mij al van het probleem af.
ExpressQuantumGrid is wel degelijk bedoeld om in te editen, zie het plaatje. Het probleem heeft echter niets met het grid te maken, vandaar dat ik er verder niets over heb gezegd.

Afbeeldingslocatie: http://www.devexpress.com/products/vcl/images/f_g401.gif

Een goede grap mag vrienden kosten.


  • whoami
  • Registratie: December 2000
  • Laatst online: 11:10
Kan je in de AfterPost van de DataSet v/d master-table het id van het (nieuwe) master record niet opvragen, en dat id dan in de BeforePost oid van de child-table gaan gebruiken om de waarde v/d Foreign key mee op te vullen?

https://fgheysels.github.io/


  • Elissen
  • Registratie: Januari 2000
  • Laatst online: 27-07 15:54
Een gebruiker voegt een master record toe (in tabel A). Aan deze record wordt in het grid geen ID toegekend, dat wordt immers door Access gedaan (tabel A heeft autonummering) bij een update.
Als je bijvoorbeeld een TADOTable gebruik dan kan je na het opslaan het net toegevoegde ID vinden in het veld :)
De gebruiker voegt vervolgens een detail record toe (in tabel B) aan de zojuist toegevoegde master record. Ook hieraan wordt geen ID toegekend, want dat moet Access weer door met zijn autonummering.
Bij het toevoegen van de detailrecord geeft Access een foutmelding dat hij de detail record refereert aan een onbekende ID in tabel A. Dat is ook logisch, want mijn applicatie heeft geen idee welke ID Access heeft gegeven aan de zojuist in tabel A toegevoegde record. En de in tabel B toe te voegen detail record moet aan deze ID refereren om de master-detail-relatie tussen beide records tot stand te brengen.
Dat zul je moeten regelen met events zoals whoami aangaf. Een mooi moment is de OnAfterInsert vind ik zelf.
Een mogelijke oplossing is de autonummering niet door Access te laten doen, maar door mijn applicatie. Maar wat voor type veld kies ik daar dan voor? Het moet eigenlijk niet Access-specifiek zijn (al weet ik dat een autonummeringsveld dat wél is). Een random GUID is misschien een oplossing, wat denken jullie daarvan?
De meeste databasesysteemen kennen een (soort van) autonummering, iig de grote jongens. Het is denk ik een tijdsverspilling op proberen iets anders op te zetten.

Notitie: In events zoveel mogelijk gebruik maken van de DataSet welke je als parameter meekrijgt. Voorbeeld: Tijdens de AfterPost kan je het ID (autonummering) van een net ingevoegd record WEL vinden in de DataSet-parameter, maar nog NIET in de tabelcomponent zelf.

  • cimbom
  • Registratie: Juni 2001
  • Laatst online: 26-04-2024

cimbom

Just Kidding

tomatoman schreef op 11 november 2003 @ 14:45:
Een mogelijke oplossing is de autonummering niet door Access te laten doen, maar door mijn applicatie. Maar wat voor type veld kies ik daar dan voor? Het moet eigenlijk niet Access-specifiek zijn (al weet ik dat een autonummeringsveld dat wél is). Een random GUID is misschien een oplossing, wat denken jullie daarvan?
Als je nummering door je programma wil laten doen, kun je wel een extra tabel aanmaken met kollommen (naam, nummer) oid.
Naam : naam tabel waarvoor je het gebruikt
Nummer : Huidige te gebruiken nummer.

Een procedure maken GetId oid. Hier geef je Naam als parameter en deze gaat in dit tabel bij record met Naam=naam de nummer ophalen die je moet gebruiken en daarna gelijk nummer in tabel met 1 verhogen.

Zo heb je een autonummering via applicatie.

Iedere keer als je een record aanmaakt voor een bepaald tabel ga je ook nummer voor dat record ophalen voordat je insert (BeforeInsert?)

Maar je kan ook zoals whoami zei via events wat regelen, zonder dat je zelf autonummering hoeft te implementeren.

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
Het wil maar niet lukken om in de AfterPost van de master record de zojuist toegekende ID op te halen.
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{ AfterPost hangt aan iedere dataset (dus niet alleen de master) }
procedure TData.AfterPost(DataSet: TDataSet);
begin
  { Eerst maar eens de cache leeggooien op  de server }
  if DataSet is TClientDataSet then
    with TClientDataSet(DataSet) do
      if ChangeCount > 0 then
        ApplyUpdates(0);

  { Als dit de master tabel is, de ID ophalen. --> Werkt niet! }
  if (DataSet = WorkOrders) and (WorkOrders.Active) then
    if DataSet.FieldByName('ID').AsInteger <> 0 then  
    begin
      { ID is altijd 0, dus deze code wordt nooit uitgevoerd }
      WorkOrders.Edit;
      WorkOrdersID.AsInteger := DataSet.FieldByName('ID').AsInteger;
      WorkOrders.Post;
    end;
end;
Zoals je ziet wordt nu na iedere wijziging de cache van TClientDataSet verwerkt op de server. Het probleem is dat de ID die de server toekent, nooit meer in de applicatie terug te vinden is. Misschien komt het omdat TClientDataSet niet slim genoeg is, maar ik krijg hoe dan ook nooit een andere ID-waarde dan 0 te zien op de client :'(

Als ik dan maar uit arren moede zelf de ID-waarden ga toekennen (dus ID in de database van autoincrement veranderen in een integer veld), moet ik nog steeds de server een nieuwe ID-waarde laten toekennen. Want als de clients zelf vrolijk ID-waarden gaan verzinnen, bestaat de kans dat twee clients dezelfde ID-waarde aan een nieuwe record toevoegen. Dat kan met zoiets als de GetID-functie waar cimbom het over heeft. Al met al is dat echter een heleboel gedoe voor zoiets simpels en daar zit ik natuurlijk niet echt op te wachten.

Een goede grap mag vrienden kosten.


  • cimbom
  • Registratie: Juni 2001
  • Laatst online: 26-04-2024

cimbom

Just Kidding

Het klopt wel dat de id altijd 0 is, want dat bepaalt dus database tijdens inserten.
Wat je wel kan doen is mischien zoals je eerst zei een random GUID kolom, toevoegen.
Bij AfterInsert event van je master datasource ga je iets implementeren die de ingevoerde id van je master record ophaalt, met in where clause jouw GUID?

  • Elissen
  • Registratie: Januari 2000
  • Laatst online: 27-07 15:54
En wat gebeurd er als je zoiets als dit doet? (losse pols werk):
code:
1
2
3
4
WorkOrders.Post;
WorkOrders.ApplyUpdates(0);
WorkOrders.RefreshRecord;
ShowMessage(WorkOrdersID.AsString);

M.a.w. kan een RefreshRecord de truuk zijn?

cimbom, de databaseengine genereerd een uniek nummer voor je (zoals je ook eerder aangaf). Het probleem in dit geval is alleen dat de client dit niet doorkrijgt (komt door het type component). In princiepe wil je gebruik maken van unieke nummers en niet van UUID's (niet leuk voor je (processor-)belasting als je grote tabellen wilt joinen).

[ Voor 21% gewijzigd door Elissen op 12-11-2003 15:45 . Reden: Duidelijker leren zijn ]


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
Bij WorkOrders.RefreshRecord krijg ik een 'Unable to find record. No key specified.' foutmelding. Datzelfde gebeurt bij TClientDataSet(DataSet).RefreshRecord.

* Tomatoman heeft op dit moment een sterke behoefte aan een boksbal.

Een goede grap mag vrienden kosten.


  • Elissen
  • Registratie: Januari 2000
  • Laatst online: 27-07 15:54
Misschien heb je wat aan dit artikel op de BDN. Gaat precies over dit onderwerp :)

* Elissen geeft tomatoman een boksbal

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 14:22

Tomatoman

Fulltime prutser

Topicstarter
Bedankt voor de boksbal :) en de link naar het verhelderende artikel. Na wat testjes aan de hand van het artikel ben ik tot de conclusie gekomen dat master-detail + TClientDataSet + AutoInc doffe ellende is.

De kern van het probleem is in feite dat de client geen unieke ID-waarden kan verzinnen en de database niet zomaar unieke ID-waarden kan aanleveren aan de client.

Daarom heb ik de belangrijkste AutoInc velden maar vervangen door TGuidField en de velden in Access geconverteerd naar numerieke velden van het type replicatie-ID (wat eigenlijk GUID's zijn). Nu kan de client unieke ID's generen, waardoor al het geklooi van ID-synchronisatie tussen client en server vervalt. Bij iedere nieuwe record verzint de client een unieke GUID met CreateGUID en alles werkt nu als een zonnetje :)

Samengevat: de oplossing is master-detail + TClientDataSet + GUID i.p.v. AutoInc

Een goede grap mag vrienden kosten.

Pagina: 1