Met de komst van .NET is als bestandsformaat voor data ingezet op XML voor kleinere hoeveelheden data en op ADO.NET voor grotere databases. De vraag die zich natuurlijk snel voordoet bij deze inleiding is wat klein en groot is. Die vraag zal door elke ontwikkelaar anders beantwoord worden, maar gebleken is wel dat de standaard XML DomDocument parser van Microsoft trager wordt met bestanden groter dan enkele megabytes. In ieder geval is het gebruik maken van de Visual FoxPro DBF bestanden nog wel mogelijk, maar dat wordt in beperkte mate ondersteund. Dit artikel gaat over ADO.NET, XML, datareaders en datasets.
Inleiding
In het vorige nummer van SDGN magazine heb ik deel 1 geschreven van IPAQ development. Dit artikel is, hoewel de titel anders doet vermoeden, het vervolg daarop. De oplossing om op een IPAQ gebruik te maken van FoxPro webservices ligt in het gebruik van ADO.NET. Verderop in dit artikel en in het hierna volgende deel kom ik daar op terug.
Data access in het verleden
Microsoft heeft tot dusver al vele technologieën gelanceerd om de vraag van ontwikkelaars naar de meest uiteenlopende data oplossingen te kunnen beantwoorden. Dergelijke vragen hebben vaak betrekking op het delen van data over verschillende platforms (niet alleen Microsoft), online opvragen van informatie, data versturen via email en vele andere. De eerste technologie was Open DataBase Connectivity (ODBC). In 1989 heeft Microsoft versie 1.0 op de markt gebracht. ODBC is een universele set van functies waarbij ODBC in staat is om die functies toe te passen op verschillende database soorten. In Windows kan een ODBC verbinding worden samengesteld die gekoppeld kan worden aan een bepaalde database. De verbinding wordt aangegeven door een verbindingsnaam die vervolgens vanuit allerlei voor ODBC geschikte toepassingen gebruikt kan worden om toegang te krijgen tot de database.
In Visual Basic is in 1992 Data Access Objects (DAO) geïntroduceerd. DAO was voornamelijk bedoeld als desktop database voor Visual Basic en kon hoofdzakelijk gegevens uitwisselen tussen Visual Basic en Access. Verder bestaat er in DAO weinig support voor relationele databases. DAO is echter wel snel, vanwege de platte opbouw van een dergelijk bestand.
In 1995 zag Remote Data Objects (RDO) het levenslicht. Deze technologie was ontwikkeld om toegang te krijgen tot remote ODBC databases. RDO was gebaseerd op DAO maar door het ontbreken van de DAO Jet engine was de performance hiervan hoger.
In 1996 is Object Linking and Embedded for Databases (OLEDB) ontworpen. Deze API ondersteunt relationele en niet relationele databases en maakt gebruik van COM technologie. OLEDB kan ook goed omgaan met niet-relationele data zoals E-mail, grafische bestanden, file systemen en anderen.
Veel van de database technologie van Microsoft van vandaag is nog steeds gebaseerd op OLEDB. Alleen C++ applicaties konden in het begin directe toegang krijgen tot OLEDB en een dergelijke manier van directe toegang leidt tot code die vaak onderhouden moet worden bij nieuwere versies van OLEDB.
Active Data Objects (ADO) is een wrapper om OLEDB, ook uitgegeven in 1996. Door deze wrapper werd het voor allerlei ontwikkeltalen eenvoudiger om gebruik te maken van de complexe functies van OLEDB. Ook is het met ADO mogelijk geworden om te werken met ‘Disconnected Recordsets’, zij het dat de mogelijkheden op dit gebied beperkt waren. Disconnected Recordsets houden in dat er data in het werkgeheugen aanwezig is en benaderd kan worden, terwijl de fysieke verbinding met de database niet meer actief hoeft te zijn.
Remote Data Service (RDS) lijkt erg op ADO, maar is bedoeld om OLEDB technologie te kunnen toepassen in web toepassingen. Er kan data worden gemuteerd op een client zonder dat er ‘gepost’ moet worden naar de webserver. Met RDS is nog meer aandacht besteed aan de mogelijkheden van disconnected recordsets dan bij ADO.
ADO.NET, geintroduceerd in 2000, is een combinatie van ADO en RDS technologie. Vanwege de ingebouwde XML ondersteuning leent dit protocol zich uitermate goed voor communicatie met andere, niet-Microsoft omgevingen en talen.
Met standaard ingebouwde features voor disconnected recordsets en XML kan ik me nog geen serieuze nadelen bedenken
Uit deze opsomming zal duidelijk geworden zijn dat er niet stil gestaan is met het uitproberen van verschillende soorten van data-access. Waar elke technologie wel zo z’n nadelen had, lijkt ADO.NET dan toch de data-access technologie van de komende jaren te worden. Met standaard ingebouwde features voor disconnected recordsets en XML kan ik me nog geen serieuze nadelen bedenken.
ADO.NET
Een groot voordeel van ADO.NET is de manier waarop disconnected recordsets ondersteund worden. Applicaties die gebruik maken van connected recordsets hebben aantal aantal nadelen:
- Connecties naar een server gebruik veel systeembronnen. In veel gevallen kan een database maar een beperkt aantal gelijktijdige connecties aan. Ook wordt de performance van een applicatie aanmerkelijk slechter bij een groot aantal connecties.
- Applicaties die een open database verbinding vereisen zijn niet per definitie goed schaalbaar. De mogelijkheid bestaat dat een applicatie met 10 gelijktijdige gebruikers nog goed functioneert maar met 100 of 250 niet meer.
Vanwege deze nadelen is er bij ADO.NET voor gekozen om zwaar in te zetten op disconnected recordsets. Gegevens worden ingelezen in een object (een dataset) waarin je vervolgens kan muteren. Zolang het object bestaat, kan de data worden gemuteerd. Het is daarbij echter niet nodig om een verbinding met de database te hebben. Zodra er weer een verbinding tot stand gebracht wordt, zullen de wijzigingen worden weggeschreven naar de database. Mochten de gegevens inmiddels al gewijzigd zijn op de server, dan zal er een foutmelding worden gegenereerd en kan de mutatie worden geannuleerd of geforceerd. In Visual FoxPro was deze manier van werken bekend als ‘Optimistic Record Locking’: mutaties konden zonder problemen worden uitgevoerd, en pas bij het wegschrijven van de mutaties terug naar de server werd gecontroleerd of er niet inmiddels een andere gebruiker was die diezelfde gegevens gemuteerd had. In tegenstelling tot ADO.NET biedt Visual FoxPro (en ook FoxPro DOS) vrijwel geen mogelijkheid om met disconnected data te werken.
ADO.NET architectuur
De hoofd onderdelen van ADO.NET bestaan uit de .NET Data Provider en het Dataset object. De dataset zal gebruik maken van de .NET Data Provider om gegevens uit een database te verkrijgen. Dit wordt duidelijker uit onderstaande afbeelding.

Het dataset object bestaat uit 5 afzonderlijke objecten: Tables, Rows, Columns, Constraints en Relations. Dit lijkt natuurlijk erg op een ‘normale’ database. Elke dataset kan meerdere tabellen bevatten, dit is echter niet verplicht.
Met de volgende code kan een dataset worden aangemaakt:
Dim dsCustomers as DataSet = New DataSet("dsCustomers")
Door gebruik te maken van de term ‘New’ wordt een nieuwe instantie gemaakt van het Dataset object. De dataset krijgt als naam ‘dsCustomers’.
DataTable object
In de ‘Tables’ property van de dataset bestaat een object van het type ‘DataTableCollection’. In dit object bestaat elke tabel als object van het type ‘DataTable’. Met de volgende code kan een DataTable object worden aangemaakt en direct worden toegevoegd aan de dataset.
Dim tabCustomerTable as DataTable = dsCustomers.Tables.Add("Customers")
Om deze tabel later weer te kunnen aanroepen kan als volgt een referentie naar de tabel worden gemaakt:
Dim tabCustomerTable as DataTable =
dsCustomers.Tables("Customers")
Het is belangrijk om te weten dat ADO.NET in principe hoofdletter gevoelig is. Het is dus mogelijk om een tabel customers en Customers te laten bestaan in dezelfde dataset.
DataColumn object
In de ‘Columns’ property van het DataTable object bestaat een object van het type ‘DataColumn‘. Elke tabel moet worden voorzien van een of meerdere kolommen, met de volgende code kan een kolom worden toegevoegd aan de eerder aangemaakte tabel.
tabCustomerTable.Columns.Add("Naam",
Type.GetType("System.String"))
Vrijwel elke tabel zal voorzien worden van een primary key kolom. In ADO.NET is het mogelijk om voor elke tabel via de property ‘PrimaryKey’ aan te geven welke kolom de primary key voorstelt.
Dim colName as DataColumn =
tabCustomerTable.Columns.Add(
"Naam", Type.GetType("System.String"))
tabCustomerTable.PrimaryKey=colName
DataRow object
In de ‘Rows’ property van het DataTable object bestaat een object van het type ‘DataRow‘. Om een tabel van gegevens te kunnen voorzien moet voor elk record een rij toegevoegd worden aan de tabel. Met de volgende code wordt een record toegevoegd aan de eerder aangemaakte tabel.
Dim rowRecord As DataRow
rowRecord = tabCustomerTable.NewRow()
rowRecord("Name") = "SDGN"
tabCustomerTable.Rows.Add(rowRecord)
RowState
Wanneer er gewerkt wordt met disconnected recordsets, is het belangrijk dat op een bepaald moment de mutaties uit deze sets terug worden gestuurd naar de server. Om te voorkomen dat elke rij terug gestuurd moet worden is het mogelijk om per rij te bepalen of er mutaties zijn uitgevoerd. Hiervoor bestaat de RowState.
De RowState kent de volgende waardes:
- Unchanged – Rij is onveranderd
- Added – Rij is toegevoegd
- Modified – Rij is gemuteerd
- Deleted – Rij is verwijderd
Om wijzigingen weg te schrijven naar de server database kan de AcceptChanges method worden aangeroepen. Het gevolg van het aanroepen van deze method is dat de rij die wordt opgeslagen als RowState weer de waarde Unchanged krijgt.
De AcceptChanges method is zowel te gebruiken op een rij, op een tabel als op een dataset. In de laatste twee situaties zullen alle wijzigingen in een keer worden weggeschreven in respectievelijk de tabel en de dataset. De volgende code beschrijft het wegschrijven van mutaties op verschillende niveau’s.
rowRecord.AcceptChanges()
tabCustomerTable.AcceptChanges()
dsCustomers.AcceptChanges()
Let op: De AcceptChanges method zorgt er alleen voor dat de mutaties definitief worden gemaakt in de disconnected recordset zelf. Dit is het beste te begrijpen wanneer je bedenkt dat de wijzigingen zich ‘in memory’ bevinden en dat ze met deze opdracht naar disk worden opgeslagen.
Het doorsturen van de wijzigingen naar de server moet plaatsvinden via de DataAdapter van de ADO.NET component.
Wijzigingen kunnen ook geannuleerd worden door middel van de RejectChanges method.
FoxPro buffering
In Visual FoxPro kan een record of tabel gebufferd worden. Wijzigingen worden dan niet rechtstreeks in de tabel weggeschreven maar worden bewaard tot het moment dat TableUpdate of TableRevert uitgevoerd wordt.
In ADO.NET is dit ook mogelijk door de methods BeginEdit en EndEdit van het DataRow object te gebruiken.
rowRecord.BeginEdit
rowRecord("Naam")="SDGN"
rowRecord.EndEdit
Met de method CancelEdit had in bovenstaand voorbeeld de mutatie van de rij geannuleerd kunnen worden.
FoxPro OldVal()
Met het commando OldVal is het mogelijk om tijdens een mutatie de oude waarde van een veld van voor de mutatie op te vragen.
In ADO.NET is de vorige waarde van een veld op te vragen via de DataRowVersion als parameter van het DataRow.Item object.
rowRecord("Naam")= "SDGN1"
rowRecord.BeginEdit
rowRecord("Naam")= "SDGN2"
Console.WriteLine(
rowRecord("Naam", DataRowVersion.Original))
Na het uitvoeren van deze code zal als naam SDGN1 worden weergegeven, de originele inhoud van het item.
Update server database
Met de hiervoor beschreven commando’s worden de wijzigingen alleen opgeslagen in het ‘lokale’ dataset object. Door gebruik te maken van de Update method van de DataAdapter worden de wijzigingen opgeslagen in de server database. Het is dan wel belangrijk dat de wijzigingen nog niet lokaal zijn opgeslagen via AcceptChanges. De DataAdapter kan dan niet meer bepalen welke gegevens gemuteerd zijn en zal dan niets updaten naar de server.
De GetChanges method van het DataSet object maakt een nieuwe dataset aan met alle wijzigingen in de dataset. Deze dataset kan vervolgens gebruikt worden om door te DataAdapter door te laten sturen naar de server. De hoeveelheid netwerk traffic is op deze manier minimaal.
Dim dsChanges As New DataSet()
dsChanges = dsCustomers.GetChanges()
myDataAdapter.Update(dsChanges)
Vanuit referentiële integriteit kan het belangrijk zijn om eerst gegevens te verwijderen, vervolgens de nieuwe gegevens toe te voegen en als laatste de mutaties aan te brengen. Als parameter van GetChanges kan de DataRowState worden meegestuurd. Deze lijkt qua werking erg op de RowState eigenschap zoals eerder beschreven.
Dim dsChanges As New DataSet()
dsChanges =
dsCustomers.GetChanges(DataRowState.Modified)
myDataAdapter.Update(dsChanges)
De hierboven afgebeelde code stuurt alleen de gemuteerde gegevens naar de server. De alternatieve eigenschappen van DataRowState zijn Deleted en Inserted.
Ter afsluiting
In dit eerste deel is ingegaan op de historie van verschillende data access methoden die de afgelopen 15 jaar op ons, ontwikkelaars, losgelaten zijn. ADO.NET lijkt de perfectionering van al die varianten en is bijzonder geschikt voor disconnected recordsets.
In het volgende deel zullen we ingaan op de te genereren code ten behoeve van het DataAdapter object en zal het gebruik van XML in combinatie met datasets worden toegelicht.
Ik kan alvast meedelen dat het mogelijk is gebleken om vanuit een Smart Device Applicatie (SDE) met behulp van een XmlDataReader direct een Visual FoxPro webservice te benaderen en gegevens te verwerken. In een eerdere sessie die ik op de Cttm gegeven heb, leek dit nog erg moeilijk haalbaar en heb ik voorgedaan hoe ik gebruik maak van een ASP.NET webservice die op zijn beurt via een COM wrapper gebruik maakt van een VFP DLL.
Vragen over dit artikelen? Wensen voor het volgende deel? Reacties? Mail maar naar mark@nedfox.nl.