Overzicht van ADO.NET
Door Charles Carroll
24 januari 2002
Ik vind de aanpak van ADO.NET fantastisch; In het begin vond ik het vreselijk, totdat ik
het hele plaatje begreep. Het kost echter best wat moeite om te begrijpen hoeveel ADO.NET verbetert, dus
haak niet te snel af.
Het verleden: De ADO manier van records lezen
ASP -> ADO -> Database (of een andere data source):
rs.Open
Do Until rs.EOF
Response.Write("Naam")
rs.MoveNext
Loop
rs.Close
bestond uit een heleboel opdrachten aan de database, meer dan de meeste mensen zich realiseren.
Laten we dit van dichtbij bekijken, waarbij we er van uit gaan dat we 300 records ophalen met vier velden:
- rs.Open is +1 opdracht
- rs.EOF test is +300 opdrachten
- Elke veld dat opgehaald wordt is minimaal 1 opdracht (300x4 = 1200 opdrachten)
- rs.MoveNext is +300 opdrachten
In totaal resulteert de bovenstaande code dus in ongeveer 1800 opdrachten aan de database, die mogelijk zelfs over het
netwerk gaan (hoewel de cachesize eigenschap het aantal opdrachten kan
verminderen in ruil voor server geheugen en een dure recordset cursor). Dit is waarom ik fanatiek het gebruik van
Getrows and Getstring aanbeval voor ADO, zie
Data ophalen met Getrows.
En nog een andere opmerking:
- Opdrachten zoals rs("Naam") vragen waardes op uit de database (via het netwerk)
en retourneren die in het data type Variant, die vervolgens dus nog geconverteerd moet worden naar het juiste
data type.
- Opdrachten zoals rs("Naam").Type vragen het data type van het veld op
uit de database (via het netwerk). Het resultaat hiervan kan erg verschillen, ook per database
(bijvoorbeeld sybase datum, oracle datum, etc.).
Er gebeurt nog veel meer onder de kap dan de meeste mensen zich realiseren, zie:
GetRows! ... Don't retrieve data any other way!.
En aangezien COM de data vast moet houden en converteren naar VB6, C++, of data types in andere programmeertalen
(die het er niet mee eens zijn wat een integer of string is) wordt een dataset als volgt opgeslagen in het geheugen.
De datatype in een recordset volgens de programmeur
| |
veld 1 |
veld 2 |
veld 3 |
| record 1 |
integer |
string |
datum |
| record 2 |
integer |
string |
datum |
| record 3 |
integer |
string |
datum |
| record 4 |
integer |
string |
datum |
| record 5 |
integer |
string |
datum |
COM Realiteit
| |
veld 1 |
veld 2 |
veld 3 |
| record 1 |
variant |
variant |
variant |
| record 2 |
variant |
variant |
variant |
| record 3 |
variant |
variant |
variant |
| record 4 |
variant |
variant |
variant |
| record 5 |
variant |
variant |
variant |
Varianten nemen natuurlijk meer geheugen in dan sterk getypeerde data en brengt een conversie last met zich mee.
De ADO.NET manier van records lezen
ASP --> ADO.NET -> DataSet:
Database openen
Data overzetten van database naar dataview in een dataset
Geen Until... Loop
De dataview bevat de data en de connectie wordt gesloten. Elke interactie vindt nu plaats met deze tabel in het geheugen,
een dataview in een dataset. De .NET onderdelen zorgen ervoor dat:
- rs("Naam") heeft GEEN interactie met de database (via het netwerk).
De waarde wordt direct uit de dataview in het geheugen gelezen. Deze waarde is daarom sterk getypeerd.
- rs("Naam").Type haalt sterk getypeerde data uit de dataset, onafhankelijk van de database
types.
- De waarden in de dataset zijn sterk getypeerd, dus nemen ze weinig geheugen in.
Er is geen conversie nodig om de data te gebruiken.
Zodra de data in de dataset zit, fungeert de dataview als een tabel/query uit de database,
maar is deze onafhankelijk van de bron, en heb je dus ook niet te maken met de eigenaardigheden
van de bron. Aangezien gemengde data bronnen, arrays en dergelijke allemaal gepresenteerd worden
in een dataset, is het makkelijk om te sorteren en filteren, en om gecombineerde queries te doen op data
uit verschillende bronnen. Een dataview in een dataset kan opgevuld worden met data uit:
- een Oracle database
- een SQL Server database
- een Access database
- XML bestanden
- een multidimensoniale array
Dataviews hebben handige eigenschappen die arrays normaal gesproken niet hebben --
Je kunt ze filteren en sorteren, en SQL queries kunnen uitgevoerd worden op de dataviews die zich in het
geheugen bevinden (niet langer in de database). Verder zijn dataviews sterk getypeerd, daar waar arrays in
ASP bestaan uit data van het type Variant, hetgeen coversielast met zich mee brengt.
Belangrijk: Als je in ADO vroeg om een veld/record data onderdeel dan was dit een Variant.
In ADO.NET zijn Datasets waarden sterk getypeerd (integer, datum, string, etc.) en waardoor ze
minder geheugen gebruiken. In ADO resulteerde een verzoek als "geef me de veldname" of "geef me het veld type"
(bijvoorbeel: rs("Naam").Type, rs("Naam").DefinedSize,
rs(2).Name, rs.RecordCount)
in een database opdracht met het daarbij behorende database verkeer. ADO.NET vraagt deze dingen gewoon aan de
dataview, en niet aan de achterliggende database. Hierdoor wordt zeer veel database verkeer vermeden.
GetRows and Getstring zijn dus niet nodig in deze omgeving.
Een andere gemak is dat een dataset meerdere dataviews kan bevatten, hetgeen ADO code elimineert waarin er
tegelijkertijd meerdere recordsets open zijn. Data van acht gegevensbronnen kan in acht verschillende dataviews
opgeslagen worden, die deel uitmaken van één dataset. En aangezien de data in een dataset
wordt opgeslagen als XML, als het geplaatst is in het geheugen, in een cache, in een sessie variabele, of een applicatie
variabele applicatie scope, draagt het nooit de extra bagage van een database verbinding. Alleen de data wordt in het
geheugen opgeslagen.
ADO mengt opmaak en code, ADO.NET doet dat niet!
Je kunt een sjabloon binden aan een dataset, zodat de data in de dataset wordt weergegeven
volgens dat sjabloon.
Easy Grid Display from Access/OLEDB Databases by Charles Carroll
DataGrid and Column Binding by Charles Carroll
Radio Button/Check Box Lists from Databases by Charles Carroll
Dropdown Lists from Database by Charles Carroll
laat een aantal manieren zien om een dataset op te maken, zonder dat daarbij constructies als
Until ... Loop gebruikt worden. Deze voorbeelden gebruiken specifiek niet een dataset,
maar er is gekozen voor het DataReader object aangezien geen van de datasets mogelijkheden (en overhead) nodig is.
Het DataReader object is daarom in deze situaties handiger.
ASP.NET Quickstart: Data Binding Server Controls
ASP.NET Quickstart: Data Access and Customization
laat veel meer voorbeelden zien van het weergeven van data uit een database zonder loops, maar verbonden aan datasets.
Visual Inheritance/CBF by Charles Carroll
laat zelfs zijn hoe de lelijkheid van ADO code tussendoor wordt opgelost door presentatie en het scheiden van code.
3: Data terugplaatsen
In ADO waren er een aantal manieren om data in een database te plaatsen:
- met rs.AddNew, een opdracht die de staat van de data moet bijhouden
- met een SQL opdracht die INSERT INTO gebruikt
- via een batch (groepsgewijze) update
Een aantal van deze mogelijkheden eiste expliciete server cursors, locks, en lelijke details.
Batch updates waren alles of niets. Als je bijvoorbeeld 50 records wilde verplaatsen en één daarvan kon niet verplaatst
worden dan werd de gehele batch geweigerd en werden er dus geen records verplaatst.
Al het verkeer tussen ADO en de achterliggende data bron gebeurde constant en was "nauw verbonden" code met
server cursors, lock types, etc. Een laag-niveau onderzoek van de onderliggende conversatie zou lelijke details tonen.
Het was allemaal nauw verbonden met de driver en werkte niet meer bij driver updates, als een andere soort data bron
gebruikt werd (bijvoorbeeld Oracle i.p.v. SQL Server), etc. Over het algemeen waren recordset operaties/code geneigd
niet meer te werken elke keer als de data bron of driver veranderde.
In ADO.NET heeft elke dataset rij een RowError eigenschap.
Alle records in een batch worden verwerkt door de achterliggende data bron. ALLEEN de records waarvoor de operatie
mislukt, krijgen de RowError eigenschap, voor de rest van de records is de operatie
gewoon gelukt.
Al het verkeer tussen ADO.NET datasets en de data bron gaat in principe via XML. Hierdoor
kan het gewoon door een firewall en zijn er nog tal van andere voordelen/neveneffecten.
RDS communiceerde vanuit Internet Explorer over Poort 1443 met SQLserver. Nu communiceert ADO.NET
met XML over poort 80 waardoor firewalls niet tussen beide kunnen komen!
In feite weet ADO.NET hoe het
INPUT DATA als parameters neemt--> ADO.NET genereert XML/SQL -> geeft aan de back-end
DELETE --> ADO.NET genereert XML/SQL -> geeft aan de back-end
etc.
Server-Side Data Access
geeft hier een voorbeeld van.
Deze datasets vragen nogal wat werk van de server. Eigenlijk is elke dataset een soort kleine database.
Relaties kunnen worden bewerkstelligd tussen tabellen/datasets, kolommen kunnen worden toegevoegd en tabellen kunnen
"on the fly" worden toegevoegd.
Je kunt met ADO.NET dus een hele set met gerelateerde data maken, en meerdere records in verschillende tabellen
toevoegen, wijzigen en verwijderen. Ook kun je relaties tussen tabellen aanleggen enz. Dit alles gebeurt ZONDER
dat er met de achterliggende data bron (bijvoorbeeld een Oracle of SQL Server database) gewerkt wordt. Pas als je
klaar bent kun je in één keer de achterliggende data bron(nen) bijwerken.
Voorheen (in ADO), moest je voor elk record dat je toevoegde, wijzigde of verwijderde communiceren met de achterliggende
data bron.
Jouw code kan bijvoorbeeld tal van records maken in verschillende tabellen, records aanpassen, records verwijderen
zonder dat er interactie met de back-end plaatsvindt. Elke vraag aan de dataview/dataset (Hoeveel records zijn het?
Hoeveel rijen zijn gerelateerd aan deze andere tabel? Wat is de sleutel van het record dat ik net heb toegevoegd?)
is lokaal, en heeft geen interactie met de back-end tot gevolgd.
DIt is slechts het topje van ijsberg (zoals Scott Guthrie zou zeggen), maar het geeft je wel een idee
van een aantal concepten in ADO.NET die veel voordelen hebben voor de schaalbaarheid en het verbuik
van bandbreedte.
© Charles Carroll
(vertaling copyright ASPNL)
|