Bob Swart (aka Dr.Bob)
Delphi 8 for .NET en Enterprise Core Objects

UML Designer, Code Generator en Object Persistence Mapper ineen!

In het kader van het thema "architectuur", wil ik deze keer laten zien hoe Borland er in geslaagd is om onderdelen van Together en Bold te integeren in .NET onder de nieuwe naam Enterprise Core Objects (ECO).

Architect
ECO zit in de Architect edities van Borland C#Builder (aka BDS 1.0) en Borland Delphi 8 for .NET (aka BDS 2.0). Wie niet de Architect editie bezit, kan altijd de 30-dagen trial editie van Delphi 8 for .NET Architect downloaden om het eens uit te proberen.

Enterprise Core Objects biedt ons de volgende functionaliteiten:

ECO Demo
Laten we dit alles eens met een klein praktijkvoorbeeld toelichten. Tijdens de Conference to the Max / Software Developers Conference bouwde ik tijdens mijn ECO sessie een kleine toepassing rondom een model om verjaardagskadootjes uit te delen. Iets wat daar een beetje op lijkt, maar in praktijk wat meer nut heeft, is een adresboekje. En dan met name een e-mail adresboekje. Met de huidige generatie virussen en trojaanse paarden is het steeds minder veilig om je adresboek direct aan je e-mail software te koppelen (als er dan een keer per ongeluk iemand "vat" krijgt op je machine, dan weten meteen al je vrienden ook dat je de pineut was - en dat lijkt me nog minder fijn eigenlijk). Ik wil dus een kleine toepassing met daarin de namen van personen, inclusief vrienden (dat zijn speciale personen) en hun lijst met e-mail adressen.

Start Delphi 8 for .NET Architect, en doe File | New - Other om de Object Repository te krijgen. In de Delphi for .NET Projects categorie is het ikoon om een Enterprise Core Objects Windows Forms toepassing te maken (het is voor Delphi 8 nog iets lastiger om ECO in combinatie met ASP.NET te gebruiken, alhoewel ze er bij Borland al wel aan werken om dit makkelijker te maken in de volgende versie van Delphi).

Object Repository en ECO Wizard

als je dubbelklikt op dit ikoon krijg je de normale "new project" dialoog, waarin je de naam en plaats van het nieuwe project kan aangeven. Verder niks bijzonders. Het grootste verschil zit het in het ECO project zelf, dat namelijk een aantal extra bestanden heeft, waaronder een CoreClassesUnit.pas en een EcoMailEcoSpace.pas unit.

Project Manager

De CoreClassesUnit.pas is de plek waar onze objecten in gedefinieerd worden, en de EcoMailEcoSpace is de unit met de code voor met maken en bijhouden van de zogenaamde EcoSpace (de ruimte waarbinnen instanties van onze objecten kunnen leven).

UML Design
Het UML Design vindt plaats in de CoreClassesUnit.pas. Klik eerst op de Model View tab van de Project Manager (de middelste tab), en expandeer dan de treeview zodat je de CoreClassesUnit helemaal uitgeklapt ziet.

Model View

Je kan nu dubbelklikken op het CoreClasses blad in de treeview om de UML Designer te zien te krijgen. Deze UML Designer komt op de plek waar je normaal de Winforms, VCL for .NET of ASP.NET Web Forms designer ziet. En om je goed te ondersteunen bevat de Tool Palette nu geen normale componenten (of code snippets), maar speciale UML teken elementen.

ECO/UML Tool Palette

Er zijn op dit moment een zestal speciale UML-controls te gebruiken om de UML diagrammen te tekenen. Wie Together wel eens gezien heeft weet dat er daar nog veel meer mogelijkheden zijn, en het ligt in de lijn der verwachtingen dat daar ook nog enkele van in toekomstige versies van ECO te vinden zullen zijn. Met de Eco Package control kunnen we een nieuwe ECO package maken - een sub-unit van de huidige ECO package die in de CoreClassesUnit zit. Dit is vooral handig als je (hele) grote ECO/UML modellen wilt bouwen, maar ook het overzicht wilt bewaren, en/of ze gewoon niet alles bij elkaar wilt zetten vanwege een management oogpunt. Met de Class control kunnen we nieuwe classes definieren, zoals een Persoon class. Behalve een naam, kan een class ook een verzameling attributen hebben (vergelijkbaar met properties) en operations (vergelijkbaar met methods). Door met de rechtermuisknop op een class te klikken kun je attributen, operations of een constructor toevoegen. De laatste wordt vooral gebruikt om attributen een default waarde te kunnen geven als het object aangemaakt wordt. We kunnen de persoon bijvoorbeeld een attribuut Naam geven, van type string. Dat ziet er dan in het UML diagram uit zoals hier onder:

Class Persoon

Behalve "normale" personen, heb ik ook vrienden en kennissen. Die zijn bijzonder, want daar wil ik het e-mail adres wel van weten. Ik kan dus een nieuwe class maken, genaamd Kennis (daar schaar ik de vrienden nu ook maar even onder). Deze class is een bijzondere versie van de Persoon, en daar kunnen we het ECO Generalization/Implementation teken object voor gebruiken: Klik op de Generalization/Implementation control, vervolgens op de Kennis class, en drag daarna naa de Persoon class om aan te geven dat de Kennis een afgeleide class is van Persoon. Het association UML teken control kan gebruikt worden om een relatie tussen twee classes aan te geven (anders dan een overervingsrelatie). Als voorbeeld maak ik nu een class Email aan. De reden voor het feit dat ik liever een aparte class Email gebruik dan Email als atttribuut toe te voegen zit hem de ervaring dat de meeste mensen meer dan één e-mail adres hebben. Andersom komt het ook wel eens voor dat een e-mail adres door meerdere personen wordt gebruikt (in een gezin bijvoorbeeld). En als een e-mail adres dat wijzigt, dan is het wel zo fijn als je dat voor alle gezinsleden tegelijkertijd kan doen, zonder dat je door je database alle attributen door moet zoeken. Om een lang verhaal kort te maken: ik heb een class Email toegevoegd, met als attribuut het emailadres van type string. Deze class kun je met het association teken object verbinden met de Kennis class (alleen van een Kennis wil ik het e-mail adres weten). De richting van de association maakt overgens niet uit: het is zowel een relatie tussen de Kennis en de Email class als andersom. Wel moeten we nog even de association een zinvolle naam geven (zoals "EmailAdres"), en properties aan de uiteinden van de associate een juiste waarden geven. Het Email eind van de association heeft default de multiplicity property op 0..1 staan: dat wil zeggen dat een Kennis kennelijk met 0 of 1 Email instanties geassocieerd kan zijn, en dus maar 0 of 1 e-mail adres heeft. In praktijk komt die 0 wel voor, maar kan die 1 veel groter zijn, dus maak ik hier 0..* van. De Kennis kant van de association staat ook per default op 0..1, maar hierbij geldt dat ieder e-mail adres in mijn database tenminste toch wel aan één kennis is gekoppeld (anders is het vrij zinloos om het e-mail adres te bewaren). Zoals eerder aangegeven kan een e-mail adres aan meerdere kennissen gekoppeld zijn, dus verander ik hier de 0..1 in 1..*. Het resulterende diagram ziet er uit als volgt:

ECO/UML Diagram

De laatste twee UML teken controls (Note en Note Link) kun je gebruiken om een stukje commentaar te schrijven, en dat met een "Note Link" te verbinden aan een onderdeel van je UML diagram. Dat kan met name handig zijn als je stukken tekst van een papieren ontwerp wilt opnemen in de UML versie van je ontwerp (zodat je het papier weer kunt recyclen). We hebben nog geen operations toegevoegd, maar dat kan later altijd nog. Laten we eerst de EcoSpace eens bekijken en wat objecten gaan gebruiken.

EcoSpace
De EcoSpace zit in unit EcoMailEcoSpace.pas. Ook hier krijgen we een designer bij, maar dat is meer een designer die alleen het deel laat zien van de niet-visuele componenten, zoals de componenten van de Enterprise Core Objects categorie. De meeste ECO componenten gebruiken we straks pas, maar dit een zinvolle plek om de zgn. PersistenceMapper componenten neer te zetten. De EcoSpace moet behalve gevuld ook opgeslagen kunnen worden. En dat gebeurt door een van de PersistenceMapper componenten aan de PersistenceMapper property toe te kennen. We kunnen kiezen uit PersistenceMapperBdp, PersistenceMapperXML, en PersistenceMapperSqlServer. De PersistenceMapperXML is de makkelijkste, en slaat de gehele EcoSpace op in een XML bestand. Je kan via de FileName property van de PersistenceMapperXML component aangeven waar dat XML bestand moet komen. Bij het opstarten van de ECO toepassing zal dan automatisch het XML bestand gelezen worden om de EcoSpace weer in zijn vorige toestand te brengen. Bij het afsluiten van de ECO toepassing zul je zelf ervoor moeten zorgen dat de EcoSpace weer in het XML bestand wprdt opgeslagen, door de UpdateDatabase method expliciet aan te roepen (zullen we zo doen). Wie liever een database gebruikt (vooral bij grotere EcoSpaces) kan beter een PersistenceMapperBdp gebruiken om via een BdpConnection component en een van de Borland Data Providers met bijvoorbeeld InterBase, DB2, Informix, Oracle, MS Access of SQL Server te werken. Er is ook al een PersistenceMapperSqlServer die standaard is ingesteld op SQL Server. Je moet er wel rekening mee houden dat ECO de structuur van je tabellen bepaald, en daar geen inmenging bij wenst, dus je kan het beste met een lege database beginnen. Welke PersistenceMapper component je ook kiest, de PersistenceMapper property van de EcoSpace unit moet naar de juiste component wijzen. Tijdens run-time kun je de waarde van deze property overigens niet aanpassen (dus je kan niet laden vanuit een XML bestand, en dan opslaan in een database - of andersom).

PersistenceMapperBdp
Het opslaan van de EcoSpace in een XML bestand is leuk voor kleine testjes, maar een serieuze toepassing maakt natuurlijk gebruik van een database om de EcoSpace in op te slaan. Zet hiervoor een PersistenceMapeprBdp component op de EcoMailEcoSpace form, en laat de PersistenceMapper property ernaar wijzen. De PersistenceMapperBdp is nog database onafhankelijk, maar je zult nu eerst een keuze moeten maken van het database type. Daarvoor kun je onder in de Object Inspector op een van de links klikken (die worden overigens ook wel "verbs" genoemd), met de keuze uit "Generic ANSI 92 setup", "Interbase [dialect3] setup", "Oracle setup", etc. Als je op een van deze links klikt dan wordt de SqlDatabaseConfig property van de PersistenceMapperBdp component gevuld met specifieke informatie voor de gekozen driver. Merk overigens op dat MS Access niet in deze lijst staat. Klik op Interbase [dialect3] setup om de EcoSpace in een InterBase database te stoppen. We hebben nu nog wel een lege InterBase database nodig, of een database die nog niet leeg is maar leeg gemaakt kan worden. Een van de manieren is om de isql command-line SQL interface naar InterBase te starten, en daar een nieuwe database te laten maken, als volgt (zorg er wel voor dat InterBase draait, maar dat spreekt vanzelf hoop ik):

  CREATE DATABASE 'C:\SDN\ECoMail.gdb' PAGE_SIZE 2048;
  EXIT;
Vervolgens kunnen we een nieuwe BDP Connection aanmaken in de InterBase categorie. Ga naar de Data Explorer, open de Interbase knoop, en klik met de rechtermuisknop om een nieuwe connection te maken. Geef die de naam EcoMail, en vul de juiste database properties in (de lokatie, username en password, etc.). Klik vervolgens op de Test knop om te zien of er ook daadwerkelijk een verbinding met de nieuwe EcoMail database gemaakt kan worden.

Borland Data Provider

Als dat eenmaal werkt, kun je de EcoMail knoop in de TreeView naar het EcoSpace form slepen, en daar loslaten. Het resultaat is een BdpConnection component dat reeds geconfigureerd is voor de EcoMail database. Laat nu de Connection property van de PersistenceMapperBdp naar deze BdpConnection wijzen, en we zijn klaar om de (nog lege) database te vullen met onze EcoSpace. Het enige dat we nog moeten doen is het data model voor de database genereren. Daarvoor moet je naar de linker onderhoek van de EcoSpace designer kijken. Hier zijn vier kleine speedbuttons te vinden, die (van links naar rechts) de volgende akties uitvoeren: create database schema (wat we nu moeten doen), evolve database (als het model is aangepast), validate model (altijd nuttig om eens te doen), en select UML packages (welke packages wil je in je database stoppen). Wij hebben nu nog alleen de eerste button nodig, om het database schema te genereren. Als je erop klikt zal de EcoMail toepassing eerst gecompileerd worden (zodat alle informatie up-to-date is), en vervolgens krijg je een dialoog te zien die je verteld of er nog bestaande tabellen in de database gevonden zijn, en zo ja of die moeten worden opgeruimd (bijvoorbeeld omdat ze een naam hebben die nu zelf gebruikt moet gaan worden). Om problemen te voorkomen kun je - zoals al eerder gezegd - het beste met een hele lege database beginnen, dan hoeft ECO alleen maar de nieuwe tabellen toe te voegen. Als je op OK hebt geklikt, zie je alleen maar in het "Message Window" een bericht dat zegt "Database schema created.", verder niks (tenzij het is mislukt natuurlijk). Je kan nu ook in de Data Explorer met de rechtermuisknop een "refresh" van de EcoMail connection doen, om de nieuwe lijst met tabellen te zien. Behalve tabellen met de naam Persoon, Kennis, en Email (voor de drie classes), is er ook een tabel met de naam EmailAdres (voor de association), en een zevental speciale ECO tabellen voor de interne "huishouding".

ECO Tabellen

ECO WinForm
Op naar het laatste onderdeel van de EcoMail demo: het user interface. Hier gebruiken we de WinForm voor. Als je goed kijkt zie je in het onderste deel van de WinForms designer al een aantal non-visuele componenten staan: de rhRoot (die naar de EcoSpace wijst waar we mee willen werken), en verder een EcoGlobalActions, EcoAutoForms, EcoListActions, en een EcoModelAwareDragDrop component. Die zorgen ervoor dat een aantal faciliteiten beschikbaar zijn, en een aantal standaard akties wat makkelijker (automatisch) uit te voeren zijn (zoals ik straks zal laten zien). Voor we verder gaan moeten we de toepassing even compileren, en de EcoSpaceType property van de rhRoot naar EcoMailEcoSpace.TEcoMailEcoSpace laten wijzen (dat is de enige keuze uit de drop-down combobox). Het gekke is dat Delphi 8 in het verleden (voor Update 2) dit automatisch deed, maar na Update 2 moet ik deze property altijd met de hand een waarde geven. Allereerst moeten we een tweetal ExpressionHandlers neerzetten, om daarmee een Object Constraint Language (OCL) expressie op de EcoSpace los te laten. Dit is een taal waar weer een standaard voor is (net als UML), en je kan er o.a. alle instanties van een bepaalde class mee zoeken, of alleen maar bepaalde velden, de relaties aflopen, etc. Gelukkig bevat Delphi een soort OCL editor waarin je door te klikken je OCL Expressie kan bouwen. Zet twee ExpressionHandlers op de WinForm, en noem ze ehKennis en ehEmail. Laat hun RootHandle property wijzen naar rhRoot - de "ingang" voor de gehele EcoSpace. Een ExpressionHandler kan ook een andere ExpressionHandler als startpunt hebben, en kan dan daarvandaan een (detail) OCL expression opbouwen bijvoorbeeld. We moeten nu nog de Expression property van beide Handlers een waarde geven. Gebruik de OCL Editor (die je krijgt door op de Expression property te dubbelklikken), en kies voor Kennis.allInstances als Expression voor de ehKennis, en Email.allInstances voor de ehEmail.

Visualisatie
Voor de visualisatie kunnen we met name controls gebruiken die een lijst representeren, zoals een DataGrid. Zet een tweetal DataGrids op de WinForm, noem de dgKennis en dgEmail, en verbindt hun DataSource properties met respectievelijk ehKennis en ehEmail. Zet de woorden "Kennis" en "Email" in de CaptionText van de DataGrids, zodat je wat makkelijker kunt zien wat de bedoeling is. Zet vervolgens twee buttons op de WinForm; onder ieder DataGrid één. Met deze buttons zullen we nieuwe object instanties aanmaken van resp. Kennis en Email. Dit kan zowel met behulp van een regel code, als volledig automatisch. Voor de Kennis kun je de volgende code schrijven voor het Click event:

  procedure TWinForm.btnNieuwNaam_Click(sender: System.Object; e: System.EventArgs);
  begin
    Kennis.Create(EcoSpace)
  end;
Dit creëert een nieuwe instantie van de Kennis class, en voegt deze toe aan de EcoSpace (het argument van de constructor). We kunnen dit echter ook automatisch laten doen, voor de tweede button (voor de Email class). We moeten daarbij gebruik maken van een tweetal speciale ECO-properties, namelijk EcoListAction en CurrencyManagerHandle. Deze properties zijn toegevoegd aan de normale Button control, door de aanwezigheid van de EcoListActions control. Op dezelfde manier is er een "normale" EcoAction property, die aanwezig is door de EcoGlobalActions control op het WinForm. Deze gebruiken we straks. De EcoListAction van de tweede Button kunnen we op Add zetten, om aan te geven dat we een nieuwe instantie willen aanmaken. We moeten alleen nog even aangeven wat voor nieuwe instantie, en daar is de CurrencyManagerHandler voor nodig. Die is te vergelijken met een ReferenceHandle en ExpressionHandle. Een CurrencyManagerHandler houdt "het huidige object" bij uit een lijst van instanties die door een ExpressionHandler zijn gegenereerd, en door een of ander visueel control (de context) worden weergegeven. Zet een CurrencyManagerHandler control op de WinForm, en noem hem cmhEmail om aan te geven dat we daarmee "de huidige instantie van Email" willen bijhouden. De CurrencyManagerHandler heeft een RootHandle, en die moet naar ehEmail wijzen (dus als "input" wordt het resultaat van de ehEmail ExpressionHandler gebruikt). Daarnaast moeten we ook de BindingContext een waarde geven, door die te laten wijzen naar de control waar het resultaat van de ehEmail in weergegeven wordt (dat is het dgEmail DataGrid). Als we eenmaal een CurrencyManagerHandler voor Email hebben, kunnen we die gebruiken. Bijvoorbeeld in de tweede Button (voor een nieuwe Email instantie). Zet de CurrencyManagerHandler property van de Button om cmhEmail, en de EcoListAction op Add. Als resultaat wordt de Text property van de Button nu ook automatisch op Add gezet, maar die kun je met de hand weer op "Nieuw" zetten natuurlijk.

UpdateDatabase
Behalve het toevoegen van een nieuwe Kennis (met één regel code) en een nieuwe Email instantie (met twee properties), moet de EcoSpace ook bewaard worden, en hiervoor moeten we nog steeds een keer de UpdateDatabase methode aanroepen. Dit kan door nog een Button te gebruiken, of door dit in de Closing event te doen, als volgt:

  procedure TWinForm.TWinForm_Closing(sender: System.Object;
    e: System.ComponentModel.CancelEventArgs);
  begin
    EcoSpace.UpdateDatabase
  end;
Nu kunnen we de EcoMail toepassing weer compileren en voor het eerst draaien. We kunnen zowel nieuwe Kennissen als nieuwe e-mail adressen toevoegen, maar ze nog niet aan elkaar koppelen.

Koppelingen
Voor het daadwerkelijk toevoegen van een Email adres aan een Kennis (oftewel: voor het maken van de association relatie) kunnen we een derde button gebruiken. Die heeft tot doel om de huidige Kennis (in het dgKennis DataGrid) te koppelen aan het huidige e-mail adres (in het dgEmail DataGrid). We hebben al een CurrencyManagerHandler voor de Email, dus moeten we er alleen nog eentje maken voor de Kennis. Noem hem cmhKennis, gebruik de ehKennis als RootHandle, en de dgKennis als BindingContext. Met de twee CurrencyManagerHandlers kunnen we de code schrijven om de Email class toe te voegen aan de lijst Email adressen van de Kennis, als volgt:

  procedure TWinForm.Button1_Click(sender: System.Object; e: System.EventArgs);
  var
    HuidigeKennis: Kennis;
    HuidigeEmail: Email;
  begin
    HuidigeKennis := cmhKennis.Element.AsObject as Kennis;
    HuidigeEmail := cmhEmail.Element.AsObject as Email;
    if Assigned(HuidigeKennis) and Assigned(HuidigeEmail) then
      HuidigeKennis.Email.Add(HuidigeEmail)
  end;
Merk op dat we het huidige e-mail adres toevoegen aan de huidige kennis, en dat we dat niet ook andersom hoeven te doen. Dus alhoewel we ook de huidige kennis aan de lijst van kennissen van het huidige e-mail adres kunnen toevoegen, hoeven we de association relatie maar in één richting aan te geven.

Laatste Loodjes
Naast de twee DataGrids met de "losse" Kennis en Email instanties, willen we nu natuurlijk ook zien welke Email addressen er allemaal bij de huidig geselecteerde Kennis horen. Daarvoor hebben we een nieuwe ExpressionHandler nodig, genaamd ehKennisEmail. Deze moet als uitgangspunt (de RootHandle property) de CurrencyManagerHandler van de Kennis gebruiken, want die wijst naar de huidige geselecteerde kennis. Vervolgens kunnen we als Expression self.Email.Emailadres gebruiken. Een derde DataGrid dat naar de ehKennisEmail wijst is voldoende om de bijbehorende Emailadressen weer te geven, zoals in het laatste figuur te zien is.

ECO WinForm Resultaat

Als we door de linkerboven dgKennis lopen zien de onderaan de bijbehorende e-mail addressen verschijnen. En via de Koppel button kunnen we de geselecteerde Kennis links met een geselecteerd Email adres rechts koppelen. Als laatste nog een gevolg van de EcoAutoForms control: als je dubbelklikt op een van de items in de DataGrids, dan verschijnt er automatisch een dynamisch opgebouwd form met daarin alle properties van het betreffende object. Hiervoor moet je nog wel EcoAutoForm property van de betreffende DataGrids op True zetten, maar het resultaat is erg handig, en biedt snel een data-entry formulier voor de betreffende object instanties. Helaas is het me nog niet duidelijk hoe deze forms eventueel aan te passen zijn (want de klant wil natuurlijk altijd net iets anders zien), maar misschien komt dat nog.

Samenvatting
In dit artikel heb ik hopelijk laten zien wat Enterprise Core Objects ons voor mogelijkheden kan bieden. We kunnen via een UML Designer een object model bouwen, dat via een EcoSpace gebruiken en met PersistenceMappers opslaan (in een XML document of een database), en de objecten in een user interface vertonen. Een van de uitbreidingen die ik graag nog zou willen zien is de mogelijkheid om ECO in een multi-tier toepassing te gebruiken (een beetje zoals Delphi's DataSnap), en combinaties met ASP.NET web forms. Daar komen we zeker nog op terug.

Wie nog vragen of opmerkingen heeft, kan die altijd per e-mail aan mij kwijt. Wie op de hoogte wil blijven van de laatste ontwikkelingen zou daarnaast zeker eens moeten overwegen om mijn Delphi for .NET clinic bij te wonen.


This webpage © 2004-2006 by webmaster drs. Robert E. Swart (aka - www.drbob42.com). All Rights Reserved.