ASPNL logo (1 kb)
zaterdag 17 mei 2008




Microsoft MVP

.NET Codewise Community
<< vorige | overzicht | volgende >>

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 z’n 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 scenario’s 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 z’n 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 scenario’s 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 z’n 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)

<< vorige | ^ naar boven | overzicht | volgende >>
copyright 2000-2007 ASPNL