Data Access in .NET
Door Michiel van Otegem
28 september 2006
De meeste applicaties maken tegenwoordig op een of andere manier gebruik van een gegevensbron zoals
een database. Hierdoor komt er een steeds grotere verantwoordelijkheid te liggen bij de
systeemonderdelen die communiceren met de gegevensbronnen, de zogenaamde Data Access technologie.
Binnen het .NET Framework wordt deze rol vervuld door ADO.NET.
De .NET strategie van Microsoft is zoals al menig maal besproken gestoeld op drie doelstellingen
voor applicaties: op ieder moment, op iedere plaats, op ieder apparaat. Deze doelstellingen zijn
voor Data Access technologie op zn zachtst gezegd uitdagend. Stel je bijvoorbeeld voor dat je werkt
met een apparaat dat slechts af en toe toegang heeft tot het bedrijfsnetwerk of het internet. Als
je dan toch met bepaalde gegevens wilt werken, moeten deze gegevens lokaal beschikbaar zijn, en
later gesynchroniseerd kunnen worden met gegevens op het bedrijfsnetwerk. Hierbij is het heel goed
denkbaar dat het betreffende apparaat niet Windows-gebaseerd is. ADO.NET is ontworpen met dit soort
problemen in het achterhoofd, en is helemaal nieuw. Om te begrijpen waarom ADO.NET volledig nieuw
is, en de huidige technologie niet aangepast is, is een kijkje terug in de tijd de moeite waard.
De geschiedenis van Data Access
Geen applicatie kan zonder een gegevensbron, en die zijn er dan ook in allerlei soorten en maten.
Sinds de jaren zeventig zijn relationele databases, databases met tabellen en records, zoals DB2,
Oracle, en SQL Server, de meeste gebruikte soort gegevensbron. Ondanks dat deze databases uitgaan
van hetzelfde principe zijn ze in gebruik nogal verschillend. Iedere database heeft z'n eigen vorm
van opslag, en functies om die opslag optimaal te benutten. Dit betekent dat ook iedere database
z'n eigen Application Programmer Interface (API) heeft, en een applicatie voorheen altijd gebonden
was aan een bepaalde database. De Open Database Connectivity (ODBC) standaard, die Microsoft
begin jaren negentig in samenwerking met andere fabrikanten ontwikkelde, was de eerste (succesvolle)
technologie die het mogelijk maakte om verschillende databases op dezelfde manier te benaderen,
zoals te zien in afbeelding 1.
Afbeelding 1, Schematische weergave van ODBC
ODBC wordt nog steeds gebruikt en wordt ook door bijna iedere database die bestaat ondersteund.
Dit geldt niet alleen op het Windows platform, maar ook op Unix, Linux, Apple, enzovoorts. ODBC
maakt het dus mogelijk om in vrijwel iedere situatie op dezelfde manier databases te benaderen.
Dit gaat echter ten koste van snelheid en functionaliteit, omdat ODBC de grootst gemene deler van
de verschillende databases gebruikt. Desondanks is ODBC zeer complex en dus niet eenvoudig te
gebruiken. ODBC is ook zeer sterk gericht op ontwikkelaars die werken met bijvoorbeeld C++, en is
vanuit bijvoorbeeld Visual Basic niet te gebruiken.
Microsoft heeft in de loop der jaren enkele andere technologieën ontwikkeld die de tekortkomingen
van ODBC moesten verhelpen, te beginnen met Data Access Objects (DAO), dat communiceert met
Microsoft Jet. Jet is de database engine waarop Microsoft Access gebaseerd is, maar kan ook gebruikt
worden om contact te maken met andere soorten databases. DAO is veel simpeler om mee te werken in
vergelijking met ODBC, en bovendien vanuit talen als Visual Basic te gebruiken. DAO is echter nog
langzamer dan ODBC als je niet werkt met een Access database, omdat de communicatie via Jet een
extra laag toevoegt. De communicatie van Microsoft Jet met ODBC is bovendien verre van optimaal.
De volgende stap in de evolutie was daarom Remote Data Objects (RDO), dat direct met de ODBC API
communiceert, in plaats van via Jet. Om niet twee verschillende methodes te hebben om met een
database te communiceren ontwikkelde Microsoft vervolgens ODBCDirect, waarmee vanuit DAO
gecommuniceerd kon worden met ODBC, maar zonder de overhead van Jet.
Midden jaren negentig werd duidelijk dat ODBC, en de daarop gebaseerde technologieën, beperkingen
hadden die steeds vaker problemen opleverde bij het maken van moderne multi-user applicaties, zoals
web applicaties. Een van de grootste problemen was snelheid en schaalbaarheid, maar ook werden
steeds vaker alternatieve gegevensbronnen gebruikt die geschikt waren voor gegevens die niet goed
in een relationele database passen. Hiërarchische structuren zoals in LDAP (Active Directory)
opgeslagen worden zijn bijvoorbeeld moeilijk onder te brengen in een relationele database zonder
de snelheid enorm aan te tasten. Omdat ODBC stoelt op het relationele model, is ODBC ook erg niet
geschikt om te gebruiken als je wilt communiceren met deze gegevensbronnen. De oplossing voor
Windows werd gezocht in de Microsoft Data Access Components (MDAC). Zoals je kunt zien in afbeelding 2
bestaat MDAC uit een verzameling technologieën die geschikt zijn voor communicatie met verschillende
soorten gegevensbronnen, niet alleen relationele databases (dus ook bijvoorbeeld de gegevensopslag
van een mailserver).

Afbeelding 2, Schematische weergave van MDAC
De belangrijkste laag van MDAC is OleDb, een object-architectuur waarmee direct met een
gegevensbron gecommuniceerd kan worden. Dit kan met een van de eerder genoemde relationele databases
zijn, maar ook Exchange Server, Active Directory en een IBM AS/400, zijn via OleDb te benaderen.
OleDb kan ook communiceren via ODBC, zodat alle databases waarvoor een ODBC driver is gebruikt kan
worden, ook als daarvoor geen specifieke OleDb implementatie is. OleDb communiceert met een gegevensbron
via een zogenaamde OleDb Provider. Alle Providers bieden dezelfde basisfunctionaliteit, maar een
specifieke Provider kan functionaliteit implementeren die specifiek is voor de onderliggende
gegevensbron. Je kunt hierbij bijvoorbeeld denken aan Stored Procedures in SQL Server. OleDb
gebruikt dus niet een grootst gemene deler aanpak zoals ODBC, maar biedt een mogelijkheid om
gegevensbron specifieke functionaliteit te implementeren. Doe je dit niet, dan is de uiteindelijke
code makkelijker te gebruiken met een andere gegevensbron, maar gebruik je de onderliggende gegevensbron
niet optimaal.
De OleDb interfaces zijn niet als COM objecten te gebruiken, en zijn daardoor alleen te gebruiken
vanuit talen zoals Visual C++. Andere talen werken in principe met OleDb via een additionele laag
die via COM te benaderen is. Deze laag is bekend als ActiveX Data Objects (ADO), en zal Visual
Basic en ASP programmeurs bekend in de oren klinken.
ADO is gecentreerd rond het Connection object, voor het maken van een verbinding met een gegevensbron,
en het Recordset object, voor de toegang tot gegevens. In de meeste gevallen fungeert het Recordset
object als een soort slimme cursor die bijhoudt welke gegevens al opgevraagd zijn en welke niet.
Wanneer door een Recordset gebladerd wordt, haalt deze de benodigde gegevens op. Dit model is sterk
gericht op client-server applicaties, aangezien er vanuit gegaan wordt dat er constant een verbinding
is met de gegevensbron. In de begin jaren van ADO was dit soort applicatie ook verruit het meest
voorkomend, dus het is niet onbegrijpelijk dat dit model gebruikt is. Dit model heeft echter als
nadeel dat een gegevensbron net zo veel connecties moet onderhouden als er gebruikers zijn, hetgeen
duur is, en de schaalbaarheid van een applicatie niet ten goede komt. Zeker voor web applicaties
met potentieel duizenden gebruikers is een dergelijk model moeilijk houdbaar. Een multi-tier
architectuur lost dit probleem op, maar vereist dat de presentatie laag geen verbinding heeft met
de onderliggende gegevensbron. In dit en andere scenarios wil je in feite gegevens opvragen, en
vervolgens de verbinding met de gegevensbron te sluiten. De gegevens kunnen dan lokaal gemanipuleerd
worden, waarna de gegevensbron bijgewerkt wordt. Om dit te kunnen faciliteren werd bij versie 2.0
van ADO de mogelijkheid toegevoegd om de verbinding te sluiten, zonder dat de Recordset daarmee
zn gegevens verloor. In feite haalde de Recordset eerst de gegevens op die het moest hebben, waarna
de verbinding (tijdelijk) verbroken werd. Hoewel dit voor sommige applicaties een oplossing was,
is deze manier van werken nooit echt van de grond gekomen. Ook methodes om de gegevens tijdelijk
op te slaan als bestand zijn nooit echt veel gebruikt, omdat ze lastig zijn in gebruik, slecht
werken als gegevens naar andere machines verstuurd moeten worden, en omdat ze een zware last leggen
op de systeembronnen. Juist dit soort scenarios spelen in de .NET strategie een belangrijke rol,
waardoor ADO geen goede basis is voor de .NET wereld.
Architectuur van ADO.NET
ADO.NET is zo ontworpen dat het optimaal gebruik maakt van de onderliggende gegevensbron, maar
desondanks schaalbaar is, en gegevens makkelijk tussen applicatie-lagen of applicaties uitgewisseld
kunnen worden. Om dit voor elkaar te krijgen bestaat ADO.NET in feite uit twee delen, een
gegevensbron specifiek deel, en een gegevensbron onafhankelijk deel. De eerste categorie zijn
zogenaamde Managed Providers, interfaces die direct met een gegevensbron communiceren en optimaal
gebruik maken van de API van de gegevensbron. Iedere Managed Provider heeft zn eigen namespace,
voor SQL Server is dit bijvoorbeeld System.Data.SqlClient, en voor Oracle System.Data.OracleClient.
Het andere gedeelte, waarin de DataSet een centrale rol heeft, is bedoeld om gegevens die uit een
gegevensbron komen op te slaan in het geheugen op een generieke manier, zodat dit te gebruiken is
door alle gegevensbronnen. De DataSet werkt als een soort replica van een gedeelte van de gegevens
in een gegevensbron. Je kunt die replica manipuleren door gegevens te veranderen, verwijderen, of
in te voegen, zonder dat je daarvoor verbinding hoeft te hebben met de onderliggende gegevensbron.
Pas als alle wijzigingen gedaan zijn, wordt de verbinding weer geopend, en wordt de onderliggende
gegevensbron bijgewerkt, zoals je kunt zien in afbeelding 3. Hierdoor is het mogelijk om met
gegevens te werken terwijl er geen verbinding is met een netwerk. Ook is het mogelijk om gegevens
naar een ander systeem te sturen. De DataSet wordt namelijk intern gerepresenteerd als XML. Omdat
XML gewoon tekst is, kan het makkelijk via het internet verstuurd worden, en eventueel zelfs naar
machines die geen .NET ondersteuning hebben, maar wel XML begrijpen en kunnen manipuleren. Verder
is een applicatie die gebruik maakt van deze manier van werken zeer schaalbaar. De beperkende
factor in dergelijke applicaties is over het algemeen het beperkte aantal verbindingen met de
gegevensbron dat ondersteund kan worden.
Afbeelding 3, Gegevens wijzigen met een DataSet
Door de verbinding zo snel mogelijk te sluiten, wordt deze alleen gebruikt als dat echt nodig is,
en hoeven andere gebruikers niet te wachten op een vrije verbinding. Het is bovendien gebleken dat
deze aanpak zeer efficiënt is, en in veel gevallen drie tot vier keer zo snel is als ADO. In
gevallen waarin je gegevens niet hoeft te wijzigen, kun je ook gebruik maken van een DataReader.
Dit is een geoptimaliseerde versie van een ADO Recordset. In tegenstelling tot de Recordset, kun
je met een DataReader echter alleen van voor naar achteren door gegevens bladeren, en kun je
gegevens ook niet wijzigen. Een DataReader, de naam zegt het al, is dus alleen bedoeld voor lees
operaties.
Zijn er ook nadelen?
Dat er geen constante verbinding met de gegevensbron is, is een belangrijk probleem. Wat gebeurt
er als de gegevens die bijgewerkt worden in de tussen tijd door iemand anders bijgewerkt zijn? In
het geval dat dit gebeurt geeft ADO.NET een foutmelding die de programmeur dient af te handelen.
Er zijn dan een aantal keuzes:
- de gewijzigde gegevens gewoon overschrijven
- de wijzigingen niet uitvoeren
- de DataSet bijwerken, en de gebruiker de keuze geven die gegevens alsnog te wijzigen
In veel gevallen zal de laatste optie de beste zijn, maar in geautomatiseerde processen ligt dit wat moeilijker.
Een ander nadeel is dat de DataSet weer zeer georiënteerd is op relationele databases. In feite is de DataSet
namelijk een relationele database in het geheugen. De vraag is dan wat we met andere soort gegevensbronnen
moeten doen. De oplossing daarvoor is betrekkelijk simpel... iets anders gebruiken dan ADO.NET.
Voor LDAP of Active Directory maak je bijvoorbeeld gebruik van de System.DirectoryServices namespace,
die daarvoor weer geheel geoptimaliseerd is. Microsoft is hiermee van het generieke model afgestapt,
omdat het daarmee niet mogelijk is om optimaal gebruik te maken van de betreffende gegevensbron.
Het .NET Framework zit verder zo in elkaar dat het veel eenvoudiger is om nieuwe onderdelen voor
dit soort zaken toe te voegen. De generieke aanpak is dus ook niet meer echt nodig.
Ten slotte
ADO.NET is een nieuwe technologie die vaarwel zegt tegen veel oude concepten. Dit gaat ten koste
van de deugden van een model waarin een constante verbinding met een gegevensbron is, maar de
problemen die dat veroorzaakt zijn elegant opgelost. ADO.NET pakt een aantal belangrijke problemen
aan die zich voordoen in gedistribueerde applicaties, waardoor ADO.NET zeer flexibel is.
Dit artikel is eerder verschenen in Windows & .NET Magazine Benelux, september 2003 (huidige naam NetOpus)
|