Results 1 to 9 of 9

Thread: Datasource koppelen aan dataset @runtime

  1. #1

    Datasource koppelen aan dataset @runtime

    Hallo hallo,

    Bij meerdere instanties van een form + datamodule moet je de datasource handmatig koppelen aan de dataset anders linkt alles naar dezelfde dataset. Is het mogelijk om een functie te schrijven die dit doet i.p.v. handmatig koppelen?

    Bij voorbaat dank!

  2. #2
    In ons framework hebben we basisclasses voor forms en data modules waarin dat gedaan wordt, al moet het nog steeds gedaan worden in elke afgeleide als die extra datasets introduceert. Daar is dan een override voor die altijd aangeroepen wordt, zodat je alleen het minimale hoeft te doen voor deze koppeling.

    Ik kan me voorstellen dat het wel mogelijk is om daar een functie voor te maken, bijvoorbeeld als volgt:
    - Instantieer eerst de datamodule, zodat er in ieder geval 1 instantie is, anders kan de design time koppeling niet worden herleid en zal de DataSet property van de data sources leeg zijn.
    - Roep een procedure aan die de koppeling moet corrigeren. Geef die de instantie van het form en de instantie van de datamodule door.
    - De procedure loopt door alle components van het form, op zoek naar data sources.
    - Bij het vinden van een datasource met een dataset wordt de class van owner van de dataset vergeleken met de class van de meegegeven datamodule.
    - Als die matchen, dan wordt de dataset van de datasource geüpdatet naar de dataset van de nieuwe datamodule.

    Of je er uiteindelijk nou heel veel mee gaat winnen...
    1+1=b

  3. #3
    Bovenstaande even uitgetypt:

    Delphi Code:
    1. procedure LinkDataModule(Form: TForm; DataModule: TDataModule);
    2. var
    3.   i: Integer;
    4.   DataSource: TDataSource;
    5.   DataSet: TDataSet;
    6. begin
    7.   for i := 0 to Form.ComponentCount - 1 do
    8.   begin
    9.     if Form.Components[i] is TDataSource then
    10.     begin
    11.       DataSource := TDataSource(Form.Components[i]);
    12.       DataSet := DataSource.DataSet;
    13.       if Assigned(DataSet) and Assigned(DataSet.Owner) and (DataSet.Owner.ClassType = DataModule.ClassType) then
    14.       begin
    15.         DataSource.DataSet := DataModule.FindComponent(DataSet.Name) as TDataSet;
    16.       end;
    17.     end;
    18.   end;
    19. end;
    In het demo-projectje zie je het werken. Het main form heeft een knopje om steeds een nieuwe datamodule te maken en te koppelen. Normaliter doe je data natuurlijk direct bij het maken van het form en de dm.
    LinkDataModule.zip
    1+1=b

  4. #4
    Ontzettend bedankt GolezTrol dit is precies wat ik zocht! Mijn dank is groot!!!

  5. #5
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747
    1 Er wordt gekeken of er datasources op het form staan
    2 Vervolgens wordt de pointer gekoppeld aan een lokale datasource
    3 Daarna wordt de lokale dataset gelezen vanuit de datasource
    4 als deze pointer is gevuld dan krijgt de lokale datasource de juiste dataset toegewezen

    Het zal misschien allemaal wel werken, maar welke winst heb je dan als @designtime al de dataset is gekoppeld aan de datasource (regel 12).
    Heeft regel 14 dan wel nut?
    Delphi is great. Lazarus is more powerfull

  6. #6
    Het is een andere instantie van de datamodule. Als je meerdere instanties van je datamodule hebt (en dat is handig, vertelde Marcel al), dan moet je runtime de datasource zetten.

    De code die ik postte is een mogelijke manier om dat te doen. Als je dus al een instantie van je datamodule hebt (laten we zeggen DM1), en je instantieert het form, dan zullen bij het laden van dat form de datasources aan de datasets op DM1 gekoppeld worden. Met mijn procedure kan je al die koppelingen omzetten naar een tweede instantie van diezelfde datamodule-class. De meegegeven datamodule is een andere (of kan een andere zijn) dan de datamodule die al aan het form gekoppeld zit.

    Maar ik wil nogmaals benadrukken dat dit waarschijnlijk alleen werkt als de koppelingen al bestaan, en daarvoor moet er dus altijd een instantie van de datamodule bestaan op het moment dat het form geladen wordt. Dat is dus nogal een voorwaarde, en bij verkeerd gebruik gaat deze oplossing dan ook hopeloos de mist in.
    1+1=b

  7. #7
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747
    Dan snap ik luigi niet wat hij precies wil.
    Quote Originally Posted by luigi
    Bij meerdere instanties van een form + datamodule moet je de datasource handmatig koppelen aan de dataset anders linkt alles naar dezelfde dataset. Is het mogelijk om een functie te schrijven die dit doet i.p.v. handmatig koppelen?
    Quote Originally Posted by Goleztrol
    Maar ik wil nogmaals benadrukken dat dit waarschijnlijk alleen werkt als de koppelingen al bestaan, en daarvoor moet er dus altijd een instantie van de datamodule bestaan op het moment dat het form geladen wordt. Dat is dus nogal een voorwaarde, en bij verkeerd gebruik gaat deze oplossing dan ook hopeloos de mist in.
    Maar als zijn probleem oplost, is het mooi meegenomen
    Delphi is great. Lazarus is more powerfull

  8. #8
    Even iets verder uitschrijven.

    Als je een DataModule of form instantieert, dan krijgt die normaal gesproken de naam die in de DFM staat, en dat is normaliter de class name minus de T. Dus je hebt een datamodule TDtmTest, en die heeft de name DtmTest.
    Er is standaard ook een globale variabele DtmTest: TDtmTest, maar die is alleen om het verwarrend te maken. Als je die weg haalt, dan kan je nog steeds een data source koppelen aan DtmTest.cdsTest.

    Als je een form instantieert, wordt ook de DFM geladen (uit een resource). Daarin staat bijvoorbeeld een TDataSource met de property DataSet = DtmTest.dasTest. DtmTest is dus de Name van de datamodule en verwijst niet naar de globale variabele.
    Om DtmTest.dasTest te herleiden moet er dus een datamodule zijn met die Name, anders lukt het verwijzen niet, en zal de property Nil blijven.

    Waneer je echter nog een instantie maakt van die datamodule, dan zal die niet dezelfde name krijgen. Die name moet namelijk uniek zijn. Die tweede instantie krijgt daarom een volgnummer en zal bijvoorbeeld DtmTest2 heten.

    Click image for larger version. 

Name:	FormDMKoppeling.png 
Views:	192 
Size:	17.9 KB 
ID:	7547

    Wanneer je dan een tweede instantie van je form maakt, zal daarvan ook weer de DFM worden geladen, en zal wederom de naam DtmTest.cdsTest herleid worden naar de eerste instantie van de datamodule, aangezien dat degene is die DtmTest heet. Luigi wil dat natuurlijk niet, want dat tweede form moet ook aan de tweede datamodule gehangen worden. Een veel gebruikte oplossing is om in runtime de koppeling expliciet te leggen door simpelweg te schrijven:
    Delphi Code:
    1. FormInstance.DataSource.DataSet := DtmTestInstance.cdsTest;

    Mijn procedure doet dat op een 'slimme' manier. Hij loopt door de componenten op zoek naar FormInstance.DataSource, kijkt naar de owner van de dataset daarvan (DtmTest), en vergelijkt de classname ervan met de meegegeven datamodule DtmTest2. Omdat de classname matcht, zoekt hij vervolgens de naam van de dataset (FormInstance.DataSource.DataSet.Name = cdsTest) en zoekt de instantie van die cdsTest op DtmTest2, om die vervolgens aan de datasource te koppelen.

    Tot zo ver is het redelijk solide. De enige voorwaarde die er is, is dat er een datamodule met de naam DtmTest (van het juiste type) bestaat op het moment dat het form zijn DFM gaat inladen. Dat gaat eigenlijk altijd goed als je het volgende doet:
    - Maak eerst de datamodule en dan pas het form. De datamodule zal de naam DtmTest krijgen als die naam nog niet in gebruik is.
    - Zorg dat je geen datamodules vrijgeeft tussen het maken van de datamodule en het form. Als DtmTest er is, dan heet je nieuwe datamodule DtmTest2. Als je dan DtmTest vrijgeeft voordat je het form maakt, dan is er geen datamodule meer met de naam DtmTest (ondanks dat je wel een instantie van TDtmTest hebt.

    In een normale, nette applicatie is die voorwaarde denk ik prima te handhaven als je weet wat je doet. De reden voor mijn waarschuwing is niet omdat de code slecht is, maar omdat het nogal een specifieke voorwaarde is, die ook lastig uit te leggen is. Als er dan ineens fouten optreden, is het misschien heel lastig om te achterhalen waardoor dat gebeurt. Met een expliciete toekenning heb je dat probleem nooit, omdat je daarbij niet afhankelijk bent van welke datamodule-instantie dan ook op het moment dat de form-dfm ingeladen wordt. De vraag is dan wat er makkelijker is: Die voorwaarde in de gaten houden, documenteren, onthouden en uitleggen aan je mede-ontwikkelaars, of gewoon een paar regeltjes expliciete code schrijven waarmee je zeker weet dat het goed gaat.
    Last edited by GolezTrol; 26-Apr-17 at 12:57.
    1+1=b

  9. #9
    Uiteindelijk heb ik het opgelost door de naam van de datamodule een lege string te maken. (Oplossing gevonden op stack overflow)

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •