Está en la página 1de 34

Pristup podacima i .

NET
1
okruženje

U ovom poglavlju napravićemo kratak pregled programa ADO.NET i pokazaćemo vam delove
koda, što je, složićete se, najbolji način da se savlada ovaj koncept. Nadamo se da će vam ovo
poglavlje obezbediti solidno razumevanje osnova funkcionisanja ADO.NET, kao i da ćete do
kraja ovog poglavlja biti ubeđeni u sve prednosti ADO.NET.
ADO.NET je najnovija tehnologija pristupa podacima u velikom nizu Microsoftovih
tehnologija. Međutim, pomenuti program razlikuje se od prethodnih tehnologija po tome što je
stvoren kao deo potpuno nove platforme koja se naziva .NET razvojno okruženje. Ova
platforma je podešena tako da izvrši revoluciju u svakoj oblasti razvoja gde je ADO.NET samo
jedan njegov aspekt. Stoga ćemo početi istraživanjem glavnih karakteristika .NET okruženja.

Razvojno okruženje .NET razvojno okruženje


Nećemo preuveličati ako kažemo da će Microsoftovo izdanje novog razvojnog okruženja i
okruženja u vreme izvršavanja – razvojno okruženje .NET – znatno unaprediti sve aspekte
programiranja u svetu Microsofta. Prednosti ove nove platforme osetiće se u oblastima kôda i
svim tipovima aplikacija koje razvijamo. Naime, .NET razvojno okruženje predstavlja enormno
veliku oblast, te stoga nismo u mogućnosti da detaljno istražimo sve njegove aspekte. Međutim, s
obzirom na to da je veoma bitno razumeti osnovne principe na kojima počiva programiranje u
.NET okruženju (pre nego što zagazimo u vode ADO.NET programiranja), proučićemo neke
njegove osnovne karakteristike.

Za detaljnije informacije o programiranju u .NET okruženju, pročitajte knjigu Professional .NET


Framework, ISBN 1-861005-56-3.
Poglavlje 1

Common Language Runtime (CLR)


Temelj na kome je izgrađeno .NET razvojno okruženje jeste CLR (engl. Common Language
Runtime). CLR predstavlja izvršno okruženje koje upravlja .NET kodom u toku izvršavanja. Na
neki način možemo ga uporediti sa razvojnim okruženjem programa Java Virtual Machine
( JVM) ili Visual Basic 6 (msvbvm60.dll). Naime, .NET razvojno okruženje mora biti
instalirano na mašini na kojoj će se izvršavati .NET programi. Međutim, za razliku od prethodno
pomenutih programa, CLR je projektovan tako da podržava kôd koji može biti napisan u
različitim jezicima. Iako je tačno da su mnogi različiti programski jezici napisani tako da
podržavaju JVM ( u ovom trenutku, više jezika nego što postoji za .NET), podrška za više jezika
nije bila jedno od primarnih razmatranja tokom projektovanja JVM-a. Međutim, u slučaju CLR-
a, pomenuta podrška predstavljala je glavni fokus pri projektovanju tog jezika.
Da bi se postigla podrška za međuplatformske jezike, svi .NET programi kompajlirani su pre
samog procesa razvijanja u jezik niskog nivoa koji se naziva međujezik (engl. Intermediate
Language) (IL). Microsoftova implementacija ovog jezika naziva se Microsoft međujezik (engl.
Microsoft Intermediate Language) ili skraćeno MSIL. Zatim se ovaj IL kôd u pravo vreme
kompajlira u matični kôd u vreme izvršavanja.To znači da se .NET izvršni i DLL kodovi,
nezavisno od originalnog jezika izvornog koda, uvek razvijaju u IL, te stoga ne postoji razlika
između komponenti koje su prvobitno bile napisane u programu C# i onih napisanih u
VB.NET-u. Ova karakteristika podržava međujezičku interoperabilnost (npr. sposobnost da se
izvede klasa koja je napisana u jednom jeziku iz klase koja je napisana u bilo kom drugom .NET
jeziku). Takođe omogućava izvođenje aplikacija bez modifikacije u neku drugu podržanu
platformu (trenutno Windows 9x/ME; Windows NT4, Windows 2000 ili Windows XP) – JIT
kompajler upravlja optimizacijama za procesor/OS mašine iz kojih se aplikacija izvodi.
Microsoft obezbeđuje kompajlere za četiri .NET jezika:
❐ C# - novi C jezik koji je dizajniran specijalno za .NET radno okruženje. Većina kodova
u ovoj knjizi je u C# jeziku.
❐ Visual Basic.NET – verzija jezika Visual Basic koja je ažurirana za .NET razvojno
okruženje (na primer, sa potpuno objektno orijentisanim karakteristikama,
strukturiranim rukovanjem izuzecima i još mnogo karakteristika koje VB programeri
zahtevaju godinama!).
❐ JScript.NET – Microsoft implementacija skript jezika JavaScript koja je ažurirana za
upotrebu u .NET razvojnom okruženju.
❐ Upravljani C++ – C++ sa „upravljanim proširenjima” za podršku .NET karakteristika
koje ne mogu biti implementirane upotrebom postojećih karakteristika jezika. Za
razliku od ostala tri jezika, C++ kompajler ne ide besplatno u paketu sa .NET
Framework SDK, već sa programom Visual Studio.NET.
❐ J# - prvobitno Visual J++ (uključujući i Microsoft proširenja za program Java poput
COM podrške) za .NET razvojno okruženje. Verzija Beta 1 programa J# objavljena je
tokom pripreme ove knjige i može se preuzeti sa Web stranice http://
msdn.microsoft.com/visualj/jsharp/beta.asp.

Veliki broj jezika (poput COBOL, Perl i Eiffel), kao i navedenih Microsoft jezika obezbeđeni su
od strane nezavisnih kompanija (detaljnije informacije za navedena tri jezika možete pronaći na
sledećim vezama:
http://www.adtools.com/donet/index.html za COBOL,
http://aspn.activestate.com/ASPN/Downloads/PerlASPX/More za Perl i
http://msdn.microsoft.com/library/techart/pdc_eiffel.htm za Eiffel).

10
Pristup podacima i .NET okruženje

Proces sakupljanja otpadaka


Jedna od najbitnijih funkcija koje nam obezbeđuje CLR jeste proces sakupljanja otpadaka (engl.
garbage collection). Naime, prilikom formiranja objekta u programima poput C i C++, memorija
koja se koristi za pomenuti proces mora biti oslobođena kako bi se mogla opet upotrebiti. U
slučaju neizvršenja, dolazi do tzv. „curenja memorije” – neupotrebljena memorija koju sistem ne
može povratiti. Naime, što je količina propuštene memorije veća, to su performanse aplikacije sve
lošije. Međutim, upravo zbog teške uočljivosti greške i njenog vremenski produženog dejstva,
praćenje pomenutih grešaka predstavlja veoma težak proces. CLR rešava navedeni problem
implementiranjem sakupljača otpadaka (engl. garbage collector). U određenim vremenskim
intervalima (kada nema prostora u raspoloživoj memoriji), sakupljač otpadaka proverava sve
reference objekta i oslobađa memoriju u objektima koji su izašli iz područja važenja i koji nisu
dostupni aplikacijama. Pored toga što se rešava problem „curenja” memorije, ovim procesom
programer je oslobođen eksplicitnog uništavanja objekata. Postoji nekoliko bitnih karakteristika
koje je poželjno zapamtiti: kao prvo, ne možemo predvideti kada će se sakupljač otpadaka
aktivirati (iako smo u mogućnosti da na silu pokrenemo mehanizam sakupljanja), te stoga objekti
mogu ostati u memoriji još određeno vreme po završetku našeg rada sa njima; kao drugo, CLR
neće raščistiti neupravljane resurse – mi to sami moramo obaviti. Uobičajen način na koji se može
obaviti prethodni proces je izlaganje metode pod nazivom Dispose koja će osloboditi sve
eksterne resurse, a koji se mogu pozvati po završetku rada sa objektom.

Commom Language Infrastructure (CLI)


Uprkos tome što je .NET razvojno okruženje trenutno dostupno samo u Windows platformama,
Microsoft je podneo podskup .NET razvojnog okruženja (CLI, zajednička jezička infrastruktura)
Evropskom udruženju proizvođača računara (ECMA) kao predlog za jedan od standarda.
Naime, posebne verzije CLI su u procesu razvijanja za operativne sisteme FreeBSD i Linux.
Slično tome, specifikacije za programske jezike C# i IL (prvobitni naziv za opšti međujezik
[engl. Common Intermediate Language] ili CIL) su takođe predložene udruženju ECMA, pa će
stoga i CLI implementacije koje ne pripadaju porodici Microsoft implementirati navedene
specifikacije.

Sklopovi
.NET kôd se razvija kao sklop (engl. assembly). Sklopovi se sastoje iz kompajliranih IL kodova i
pritom moraju da sadrže jednu primarnu datoteku (osim u slučaju dinamičkih sklopova koji se u
potpunosti skladište u memoriji). Pomenuta datoteka može biti izvršna datoteka (.exe), DLL ili
kompajlirana ASP.NET Web aplikacija ili Web servis. Sklop može da sadrži resursne datoteke
(poput slika i ikona) i ostale module koda, kao i svaka primarna datoteka. Međutim, kao
najbitnije, sklopovi mogu da sadrže metapodatke. Pomenuti metapodaci sastoje se iz dva dela: tip
metapodatka sadrži informaciju o svim izvezenim tipovima i njihovim metodama koji su
definisani u sklopu. .NET sklopovi, kao i IL kodovi, sadrže deo koji se zove manifest. Ovaj deo
sadrži metapodatke sklopa (engl. assembly metadata) ili informaciju o samom sklopu, kao što su broj
verzije i broj izgradnje.
Pomenuti metapodaci dozvoljavaju sklopu da u potpunosti bude samoopisiv. Naime, sklop
sadrži sve informacije potrebne za instaliranje i aktiviranje aplikacije, te stoga nema potrebe za
bibliotekama tipa ili registara unosa. Pomenuti proces instaliranja može biti jednostavan kao i
proces kopiranja sklopa na ciljnu mašinu. Samim tim što sklop sadrži informacije o verziji, više
verzija iste komponente mogu se instalirati zajedno na istoj mašini. Upravo ova mogućnost
rešava problem poznat kao „DLL pakao” (engl. DLL Hell), gde aplikacija koja instalira novu
verziju postojeće komponente prekida programe koji koriste staru verziju.

11
Poglavlje 1

Common Type System


Common Type System (CTS) predstavlja osnovu na kojoj su izgrađene međujezičke
karakteristike CLR-a. Da bi klase koje su definisane u različitim jezicima mogle međusobno da
komuniciraju, potreban im je zajednički način predstavljanja podataka – zajednički skup tipova
podataka. Svi prethodno definisani tipovi koji su dostupni u IL, definisani su u CTS-u. To znači
da se svi podaci u .NET kodu skladište u istim tipovima podataka, s obzirom na to da su svi
.NET kodovi kompajlirani u IL-u.
CTS pravi razliku između dve osnovne kategorije tipova podataka – tipovi vrednosti (engl. value
types) i tipovi reference (engl. reference types). Tipovi vrednosti (uključujući i većinu ugrađenih
tipova, kao i struktura i nabrajanja) sadrže sopstvene podatke. Na primer, promenljiva tipa celog
broja skladišti ceo broj direktno u programski stek. Tipovi reference (uključujući i String i
Object, kao i nizove i većinu korisnički definisanih tipova poput klasa i interfejsa) skladište
samo referencu za sopstvene podatke u steku – sami podaci skladište se u različitim oblastima
memorije koje su poznate kao hip (engl. heap). Razlika između pomenutih tipova je evidentna u
slučaju prenošenja parametara metodi. Svi parametri metode se prema podrazumevanim
vrednostima prenose pomoću vrednosti, a ne reference. Međutim, u slučaju tipova reference,
vrednost ne predstavlja ništa više od reference za lokaciju na hipu gde se skladište podaci. Kao
rezultat, parametri tipa reference ponašaju se slično kao argumenti koje prenosi referenca –
promenom vrednosti promenljive unutar metode takođe će uticati na originalnu promenljivu.
Ovo je veoma bitna karakteristika koju bi trebalo da zapamtite ako nameravate da prenosite
ADO.NET objekte u metode.

Opšte specifikacije jezika


Jedna bitna napomena vezana za CTS jeste da svi jezici ne izlažu sve karakteristike. Na primer,
C# sadrži bit predznaka tipa podatka (sbyte) koji nije dostupan u programu Visual Basic.NET.
Zbog ovoga mogu nastati problemi vezani za interoperativnost jezika, te stoga opšte
specifikacije jezika odnosno, CLS (engl. Common Language Specification), definišu podskup CTS-a
koji svi kompajleri moraju da podržavaju.Takođe je moguće izložiti karakteristike koje nisu
sadržane u CLS-u (na primer, klasa C# sa javnim svojstvom tipa sbyte). Međutim, veoma je
bitno zapamtiti da karakteristike poput pomenutih ne moraju biti dostupne iz drugih jezika – u
ovom primeru nismo u mogućnosti da pristupimo klasi iz VB.NET-a.

Biblioteke .NET klasa


Sledeća karakteristikama predstavlja najvažniju karakteristiku od svih pomenutih – ogroman
skup biblioteka klase za obavljanje svakog mogućeg zadatka u programiranju. Klase i ostali
tipovi u okviru .NET radnog okruženja organizovane su u prostore imena (engl. namespaces)
slične onima u Java paketima. Pomenuti prostori imena mogu biti ugneždeni unutar drugih
imenovanih prostora, čime nam je omogućeno da identifikujemo svoje klase i razlikujemo ih od
klasa sa istim nazivom koje potiču od druge kompanije.
Zajedno sa .NET radnim okruženjem, Microsoft obezbeđuje ogroman skup klasa i drugih
tipova, najviše u okviru imenovanog prostora System ili u okviru jednog od mnogih
ugneždenih imenovanih prostora. U ovu kategoriju spadaju i primitivni tipovi poput celih
brojeva, gde tipovi C# int i VB.NET Integer predstavljaju samo pseudonime za tip
System.Int32. Međutim, u pomenutu grupu spadaju i klase koje se koriste za Windows
aplikacije, Web aplikacije, servise direktorijuma, pristup datotekama, uključujući i pristup
podacima. Pomenute klase pristupa podacima poznate su kao ADO.NET. Naime, .NET
programiranje predstavlja efektivno programiranje sa bibliotekama .NET klasa – nemoguće je
napisati program u jeziku C# ili VB.NET koji ne koristi ove biblioteke.

12
Pristup podacima i .NET okruženje

Nije valjda još jedna u nizu tehnologija pristupa


podacima?
Uzimajući u obzir revolucionarnu prirodu .NET razvojnog okruženja i činjenicu da se nove
biblioteke klasa primenjuju u gotovo svim oblastima programiranja, sasvim je razumljivo da su
projektanti prinuđeni na učenje još jedne u nizu tehnologija pristupa podacima. Konačno,
postalo je sasvim uobičajeno da se skoro svake godine pojavi nova strategija pristupa podacima.
Međutim, ipak nije vreme da zaboravite sve što ste do sada naučili – ODBC i OLE DB možete
upotrebljavati iz programa ADO.NET i budite uvereni da će proći još neko vreme pre nego što
će ADO.NET direktno pristupati izvorima podataka. Čak i program ADO, za koji ADO.NET
na neki način predstavlja zamenu, može biti upotrebljen u određenim situacijama. Zato
nalazimo da je poučno proučiti istorijat razvoja pristupa podacima tokom proteklih godina.

Kratak istorijat pristupa podacima


U početku, programski pristup bazama podataka sprovodio se kroz matične biblioteke, poput
DBLib za SQL Server i Oracle Call Interface (OCI) za Oracle. Upravo ovim je i omogućen brz
pristup bazama podataka zato što nije postojao dodatni sloj – kodovi su bili napisani za direktan
pristup bazi podataka. Međutim, to je podrazumevalo da su projektanti morali da nauče novi
skup rutina API za svaki sistem baze podataka kojoj žele da pristupe, a u slučaju da je trebalo
ažurirati aplikaciju kako bi funkcionisala u različitom sistemu baze podataka, svi kodovi pristupa
podacima su morali da se promene.

ODBC
Microsoft je ranih 90-ih godina u saradnji sa drugim kompanijama razvio tzv. povezivost otvorenih
baza podataka (engl. Open Database Connectivity) ili skraćeno ODBC, i to kao rešenje prethodno
napomenutom problemu. Ovim se obezbedio zajednički sloj pristupa podacima koji se mogao
koristiti za pristupanje skoro svakom sistemu upravljanja relacionom bazom podataka (RDBMS).
Naime, ODBC koristi specifičan RDBMS upravljački program za komuniciranje sa izvorom
podataka. ODBC upravljački program (engl. Driver Manager) učitava i upravlja pomenutim
upravljačkim programom (ponekad su u pitanju samo ovojnice matičnih API poziva). Ovim je
takođe obezbeđena karakteristika poput udruživanja veza (engl. connection pooling) – sposobnost da
se ponovo upotrebe povezivanja, umesto uništavanja istih pri njihovom zatvaranju i stvaranja
novog povezivanja svaki put kada se pristupi bazi podataka. Naime, aplikacija komunicira sa
upravljačkim programom kroz standardni API, te stoga (teorijski gledano), ako želimo da
ažuriramo aplikaciju kako bismo se povezali sa različitim sistemom upravljanja relacionom bazom
podataka (RDBMS), potrebno je da promenimo samo detalje povezivanja (u praksi, postojale su
određene razlike u SQL dijalektu). Međutim, jedna od najbitnijih karakteristika ODBC-a je
činjenica da je ODBC bio otvoreni standard prihvaćen čak i od strane asocijacije Open Source.
Kao rezultat, ODBC upravljački programi razvijeni su za mnoge sisteme baze podataka kojima se
ne može direktno pristupiti pomoću kasnijih tehnologija pristupa podacima. Kao što ćemo ubrzo
videti, ovo znači da će ODBC tek odigrati svoju ulogu u konjukciji sa ADO.NET-om.

DAO
Jedan od problema vezanih za ODBC jeste da je dizajniran za upotrebu iz jezika niskog nivoa
poput C++. Naime, uporedo sa rastom važnosti programa Visual Basic, rasla je i potreba za
tehnologijom pristupa podacima koji bi se koristili na jednostavniji način iz VB-a. Ovaj problem
rešen je u VB 3 stvaranjem objekata pristupa podacima (Data Access Objects – DAO). Naime, DAO
je obezbedio jednostavan model objekta za komuniciranje sa Jetom, mehanizmom baze
podataka u pozadini Microsoft Access radne površine baze podataka. DAO je optimizovan za
Access (iako se može upotrebiti za povezivanje sa ODBC izvorima podataka) i predstavlja
najbrži način za komuniciranje sa Accessom iz VB 6.

13
Poglavlje 1

RDO
Zbog optimizacije za Access, DAO je postao prespor za upotrebu sa ODBC izvorima podataka.
Da bi rešio problem, Microsoft je u verziji VB 4 (32-bitna verzija) predstavio tzv. udaljene
objekte podataka (Remote Data Objects – RDO). Naime, RDO obezbeđuje jednostavan model
objekta sličan DAO modelu koji je dizajniran specijalno za pristup ODBC izvorima podataka. U
suštini, RDO predstavlja tanak omotač preko ODBC API.

OLE DB
Još jedan veliki zemljotres u svetu tehnologija pristupa podacima izazvalo je izdanje OLE DB-a.
Po strukturi, OLE DB liči na ODBC: komunikacija sa izvorom podataka vrši se preko OLE DB
dobavljača (sličan konceptu ODBC upravljačkih programa), koji su dizajnirani za svaki podržani
tip izvora podataka ponaosob. Naime, OLE DB dobavljači implementiraju skup COM interfejsa
koji dozvoljavaju pristup podacima u standardnom formatu red/kolona. Aplikacija koja koristi
pomenute podatke poznata je pod nazivom OLE DB korisnik (engl. OLE DB consumer). Kao što
ovi standardni dobavljači podataka izvode podatke iz izvora podataka i čine ih dostupnim kroz
OLE DB interfejse, tako i OLE DB sadrži određeni broj dobavljača usluga. Ovim se formira
''srednji niz'' OLE DB arhitekture, obezbeđujući usluge koje se upotrebljavaju sa dobavljačem
podataka. U pomenute usluge spadaju i udruživanje veza, registrovanje transakcija (sposobnost
da se automatski registruju MTS/COM komponente u okviru MTS/COM transakcije),
postojanost podataka, klijentsku manipulaciju podacima (Client Cursor Engine ili CCE),
hijerarhijske skupove zapisa (oblikovanje podataka) i pravljenje podataka na udaljenoj mašini.
Međutim, stvarna inovacija koja se krila iza koncepta OLE DB bila je Microsoftova strategija za
univerzalni pristup podacima (engl. Universal Data Access – UDA). Naime, ideja koja se nalazi iza
pomenutog koncepta UDA jeste da se podaci skladište na mnogim mestima – e-pošta, Excel
tabelarni proračun, Web stranice itd. – kao uostalom i u tradicionalnim bazama podataka, pa bi
stoga trebalo da smo u mogućnosti da programski pristupimo svim pomenutim podacima, i to
kroz jednu unifikovanu tehnologiju pristupa podacima. Upravo OLE DB predstavlja osnovu za
Microsoftovu implementaciju ove strategije. Broj OLE DB dobavljača postepeno se povećavao
da bi se pokrili kako sistemi relacionih baza podataka (čak i MySQL baza podataka poseduje
OLE DB dobavljač), tako i nerelacioni izvori podataka poput Exchange 2000Web Store,
datoteke Project 2000 i IIS virtuelni direktorijumi. Međutim, Microsoft je pre pojave ovih
dobavljača obezbedio široki spektar podrške za OLE DB – OLE DB dobavljač za ODBC
upravljačke programe. Stoga je od samog početka bilo jasno da se OLE DB mogao koristiti za
pristup bilo kom izvoru podataka koji sadrži ODBC upravljački program. Uostalom, pomenuta
taktika prihvaćena je i u ADO.NET-u.

ADO
ActiveX Data Objects (ADO) predstavlja tehnologiju koja je svoj naziv pozajmila programu
ADO.NET (iako su razlike između pomenutih tehnologija prilično velike). Naime, ADO je samo
jedan od OLE DB korisnika – tanak sloj koji omogućava korisnicima jezika visokog nivoa poput
VB-a i skript jezika da pristupe OLE DB izvorima podataka kroz jednostavan model objekta;
ADO je za OLE DB skoro isto što je i RDO bio za ODBC. Popularnost ADO-a leži u činjenici da
je omogućio velikom broju projektanata Visual Basica, ASP-a i Visual J++ jednostavan pristup
podacima na različitim lokacijama. Ako je OLE DB bio osnova na kojoj je izgrađen UDA, ADO
je bio odora u kojoj se on predstavljao većini projektanata. ADO i dalje predstavlja ispravan izbor
za projektante koji rade u .NET razvojnom okruženju. Samim tim što je većina klasa i koncepata
veoma slična, poznavanje ADO-a predstavlja veliku prednost pri učenju ADO.NET-a. U ovom
poglavlju ćemo se pozabaviti detaljnim upoređivanjem ADO i ADO.NET-a.
A sada ćemo proučiti karakteristike ADO.NET-a.

14
Pristup podacima i .NET okruženje

Uvod u ADO.NET
Iako smo ADO.NET predstavili kao neku vrstu neizbežnosti koja prati razvoj .NET okruženja i
pojavu novog pristupa podacima API, i dalje nam preostaje da objasnimo razlog. Uostalom,
sasvim je moguće nastaviti sa upotrebom ADO-a u .NET aplikacijama kroz COM
interoperabilnost. Međutim, postoje opravdani razlozi zašto ADO nije predviđen za novo
programsko okruženje. Stoga ćemo na brzinu pogledati zašto je korišćenje ADO.NET-a
efikasnije od pozivanja ADO-a iz .NET okruženja, pre nego što započnemo detaljno istraživanje
arhitekture ADO.NET-a.

Prednosti korišćenja upravljanih klasa


Ako koristimo .NET, COM interoperabilnost dodaje podršku našoj aplikaciji koja, samim tim,
produžuje vreme obrade. Naime, .NET komunicira sa COM komponentama kroz proksije koji
se nazivaju Runtime Callable Wrappers (ovojnice koje se pozivaju u vreme izvršavanja), dok
pozivi metodama moraju biti prosleđeni iz proksija do COM objekta.Takođe, COM
komponente ne mogu koristiti prednosti CLR-a poput JIT kompilacije i upravljanog okruženja
izvršavanja; one moraju biti kompajlirane u matičnom kodu pre procesa instalacije. Upravo ovo
zahteva posedovanje prave .NET biblioteke klasa za pristup podacima.

Međujezička podrška
Treba znati da ADO nije dizajniran za međujezičku upotrebu. Naime, ciljni korisnici ovog
programa bili su VB programeri. Kao rezultat, ADO koristi veliki broj opcionih parametara
metoda koje podržavaju VB i VB.NET, ali ne i C jezici poput C#. Stoga, ukoliko koristite ADO
iz jezika C#, biće potrebno da specifikujete sve parametre u pozivima metoda; na primer, ako
pozovete metod Connection.Open, a pritom ne želite da specifikujete opcije, potrebno je da
unesete i parametar adConnectUnspecified. Iz priloženog se može videti da ADO
programiranje u .NET okruženju oduzima prilično vremena.

Jednostavnija arhitektura
Kao što smo napomenuli, ADO predstavlja samo tanak sloj preko OLE DB-a. Samim tim, ADO
građa postaje nezgrapnija dodavanjem slojeva između aplikacija i izvora podataka. Iako će veliki
broj ADO.NET kodova i dalje koristiti OLE DB, ovaj broj će se sve više smanjivati pojavom
originalnih .NET dobavljača podataka. Naime, tamo gde postoje originalni dobavljači,
ADO.NET radi mnogo brže od ADO-a, iz razloga što dobavljači komuniciraju direktno sa
izvorom podataka. U sledećem odeljku sledi detaljnije razmatranje ADO.NET arhitekture.

XML podrška
Jedna od glavnih karakteristika .NET razvojnog okruženja jeste podrška za XML. XML
predstavlja standardni i perzistentni format kroz .NET razvojno okruženje. Iako je ADO imao
određenu podršku za XML počev od verzije 2.1 pa nadalje, ona je bila prilično ograničena i
zahtevala je da XML dokumenta budu u tačno određenom formatu. Kratak pregled ADO.NET
podrške za XML možete pronaći u nastavku ovog poglavlja, dok detaljnije proučavanje sledi u
poglavlju 8.

15
Poglavlje 1

Optimizovani model objekta


Veoma je bitno imati na umu da je .NET razvojno okruženje usmereno na razvoj raspodeljenih
aplikacija, a posebno Internet aplikacija. U ovom kontekstu, jasno je da su određeni tipovi
povezivanja bolji od drugih. U Internet aplikaciji, nije poželjno dugo držati otvorenu vezu, jer
može doći do zagušenja, s obzirom na konstantno povećavanje broja otvorenih veza, čime
dolazi do uništavanja podesivosti. Naime, ADO se nije zalagao za skupove zapisa bez direktnog
povezivanja, za razliku od ADO.NET-a koji poseduje različite klase za povezani i nepovezani
pristup i pritom ne dozvoljava ažurirane povezane skupove zapisa. Detaljnije informacije slede u
nastavku ovog poglavlja.

Kratak pregled građe ADO.NET-a


Sada kada smo vas, nadamo se, ubedili u sve prednosti korišćenja ADO.NET-a, pozabavićemo
se načinom na koji ADO.NET funkcioniše. ADO.NET model objekta sastoji se iz dve osnovne
komponente: skupa podataka (engl. DataSet) koji nije povezan sa izvorom podataka i koji ne
zahteva poznavanje porekla podataka koje sadrži; i .NET dobavljača podataka (engl. .NET data
provider). Naime, .NET dobavljači podataka nam omogućavaju da se povežemo sa izvorom
podataka i da izvršimo SQL komandu.

.NET dobavljači podataka


U vreme pisanja ove knjige, postojala su tri .NET dobavljača podataka: za SQL server, OLE DB
izvore podataka i za ODBC izvore podataka. Svaki dobavljač postoji u imenovanom prostoru u
okviru imenovanog prostora System.Data i sastoji se od određenog broja klasa. Nešto kasnije
ćemo detaljno proučiti pomenute dobavljače.

Komponente dobavljača podataka


Svaki .NET dobavljač podataka sastoji se od četiri glavne komponente:
❐ Konekcija – koristi se za povezivanje sa izvorom podataka.
❐ Komanda – koristi se za izvršavanje komande u vezi sa izvorom podataka i za izdvajanje
objekata DataReader ili DataSet, kao i za izvršavanje komandi INSERT, UPDATE
ili DELETE.
❐ DataReader – povezani skup rezultata samo za prosleđivanje i čitanje.
❐ DataAdapter – koristi se za popularisanje skupa DataSet sa podacima iz izvora
podataka, kao i za ažuriranje izvora podataka.

.NET dobavljač podataka

Veza Izvor
podataka

Komanda

DataReader

DataAdapter

16
Pristup podacima i .NET okruženje

Primetićete da su ove komponente zasebno implementirane pomoću .NET dobavljača; na


primer, ne postoji klasa Connection. Umesto toga, SQL Server i OLE DB dobavljači
implementiraju klase SqlConnection i OledbConnection. Pomenute klase direktno potiču
iz System.ComponentModel.Component, jer ne postoji apstraktna klasa Connection, već
se implementira isti interfejs IDbConnect (u imenovanom prostoru System.Data). O ovome
ćemo detaljnije govoriti u nastavku ovog poglavlja.

Klase povezivanja
Klase povezivanja (engl. connection classes) veoma su slične objektu ADO Connection i koriste
se za predstavljanje veze sa specifičnim izvorom podataka. Klase povezivanja skladište
informacije koje su potrebne ADO.NET-u za povezivanje sa izvorom podataka u obliku
poznatog niza povezivanja (kao i u programu ADO). Svojstvo ConnectionString interfejsa
IDbConnection sadrži informacije poput korisničkog imena i lozinke korisnika, naziv i
lokaciju izvora podataka sa kojim se povezuje itd. Klase povezivanja takođe sadrže metode za
otvaranje i zatvaranje veza, kao i za početak transakcija i, na kraju, svojstva za podešavanja
perioda za vremensku kontrolu veze i vraćanja veze u prvobitno stanje (otvoreno ili zatvoreno).
U odeljku o postojećim .NET dobavljačima govorićemo o načinu na koji možete otvoriti veze
sa specifičnim izvorima podataka.

Klase komandi
Klase komandi izlažu interfejs IDbCommand i slične su ADO objektu Command. Upotrebljavaju
se za izvršavanje SQL iskaza ili sačuvanih procedura u izvoru podataka. Klase komandi, kao i
ADO objekat Command, takođe poseduju svojstvo CommandText koje sadrži tekst komande
koju je potrebno izvršiti u odnosu na izvor podataka, kao i svojstvo CommandType koje ukazuje
na to da li je komanda SQL iskaz, naziv sačuvane procedure ili naziv tabele. Postoje tri zasebna
izvršna metoda – ExecuteReader koji vraća objekat DataReader, zatim metod
ExecuteScalar koji vraća određenu vrednost i, na kraju, metod ExecuteNonQuery koji se
upotrebljava kada se podaci ne vraćaju kao odgovor upitu (na primer, za iskaz SQL UPDATE).
Klase komandi poseduju i kolekciju Parameters – kolekcija objekata koja predstavlja
parametre koje je potrebno proslediti u sačuvanu proceduru. Pomenuti objekti izlažu interfejs
IDataParameter i formiraju deo .NET dobavljača. Naime, svaki dobavljač poseduje zasebnu
implementaciju interfejsa IDataParameter (i IDataParamterCollection):

SQLClient.NET dobavljač podataka


Komanda Sql:
Komanda IDb

Kolekcija parametara Sql


Kolekcija parametara IData

Parametar Sql:
Parametar IData

17
Poglavlje 1

DataReader
Komponenta DataReader predstavlja odgovor ADO.NET-a na povezani skup zapisa (engl.
recordset) u programu ADO. Međutim, DataReader je samo za prosleđivanje i čitanje (engl.
forward-only, read-only) – ne postoji mogućnost njegove upotrebe za ažuriranje izvora podataka.
Samim tim nam je omogućen ekstremno brz pristup podacima koje želimo da ponovimo samo
jednom, te je stoga preporučljiva upotreba objekta DataReader (umesto objekta DataSet)
gde god je to moguće. Naime, objekat DataReader može biti vraćen samo iz poziva metodu
ExecuteReader određenog komandnog objekta; ne postoji mogućnost direktnog formiranja
objekta. Upravo zbog ovoga je potrebno eksplicitno formiranje komandnog objekta, za razliku
od principa koji važi u programu ADO, gde je moguće pozvati objekat RecordSet bez
eksplicitnog stvaranja objekta Command. Samim tim, ADO.NET model objekta je
transparentniji od ''linearne'' hijerarhije programa ADO.
DataAdapter
Poslednja komponenta .NET dobavljača podataka je DataAdapter. Objekat DataAdapter
ponaša se kao most između nepovezanog objekta DataSet i izvora podataka. Zahvaljujući tome
ova komponenta otkriva dva interfejsa: IDataAdapter koji definiše metode za popularisanje
objekta DataSet sa podacima iz izvora podataka, kao i za ažuriranje izvora podataka sa
promenama objekta DataSet koje su obavljene u klijentu. Drugi pomenuti interfejs,
IDbDataAdapter definiše četiri svojstva tipa IDbCommand. Svako od pomenutih svojstava
podešava ili vraća komandni objekat koji specifikuje koju je komandu potrebno izvršiti kada se
postavi upit izvoru podataka ili kada se isti ažurira:

SqlClient.NET dobavljač podataka

SqlDataAdapter:
IDataAdapter,
IDbDataAdapter

SelectCommand SqlCommand:
IDbCommand

Inser tCommand SqlCommand:


IDbCommand

UpdateCommand SqlCommand:
IDbCommand

DeleteCommand SqlCommand:
IDbCommand

U slučaju da pokušate da ažurirate izvor podataka, a da pritom ispravna komanda nije


specifikovana, doći će do generisanja greške. Na primer, ako pokušate da pozovete Update ili
DataSet tamo gde je dodat novi red, a da prethodno niste specifikovali komandni objekat
InsertCommand za objekat DataAdapter, pojaviće se sledeća poruka o grešci:
Unhandled Exception: System.InvalidOperationException: Update requires a valid
InsertCommand when passed DataRow collection with new rows.
Nešto kasnije ćemo se pozabaviti načinom na koji možemo da izbegnemo greške.

18
Pristup podacima i .NET okruženje

Postojeći dobavljači podataka


Trenutno postoje tri .NET dobavljača podataka koji su nam dostupni, čime nam je i omogućen
pristup bilo kom tipu izvora podataka kome se može pristupiti upotrebom verzije ADO 2.1.
Razlog što pominjemo verziju 2.1, a ne verziju 2.5 programa ADO, je u tome što za interfejse
OLE DB 2.5 – IRow, IStream itd. (koji su izloženi pomoću ADO objekata Record i Stream)
ne postoji podrška dobavljača OleDb. To znači da ćemo i dalje morati da koristimo ''klasični''
ADO sa izvorima podataka poput Web direktorijuma i Exchange 2000 Web Store, sve dok
ADO.NET ekvivalenti za OLE DB dobavljače Internet izdavaštva (MSDAIPP) i Exchange 2000
(ExOLEDB) ne preuzmu vodeću ulogu.

SqlClient dobavljač
Dobavljač SqlClient predstavlja sastavni deo paketa ADO.NET i nalazi se u imenovanom
prostoru System.Data.SqlClient. Pomenuti dobavljač može se koristiti za pristup bazama
podataka SQL Server 7.0 (kao i kasnije verzije) ili, pak, za pristup bazama podataka MSDE.
Međutim, SqlClient dobavljač se ne može upotrebljavati sa SQL Server 6.5 ili prethodnim
verzijama baza podataka, te je stoga potrebno koristiti OleDb. NET dobavljač sa OLE DB
dobavljačem za SQL Server (SQLOLEDB) kada želite da pristupite ranijoj verziji SQL Servera.
Ako ste u mogućnosti da upotrebite SqlClient dobavljač, preporučujemo da to i uradite zato
što se korišćenjem OleDb dobavljača dodaje još jedan sloj vašem kodu pristupa podacima i
koristi COM interoperabilnost (OLE DB je zasnovan na COM-u).
Sve klase koje se nalaze u okviru dobavljača SqlClient počinju sa ''Sql'', pa zato možemo
reći da je klasa povezivanja SqlConnection, komandna klasa SqlCommand itd. Sada ćemo
pregledati ADO.NET kôd i otvoriti vezu sa pubs bazom podataka na SQl Serveru. Kao i kôd
većine kodova u ovom poglavlju (zapravo, u celoj knjizi), koristićemo C#:

Using System.Data.SqlClient; // The SqlClient imenovani prostor


dobavljača
// Formiraj vezu prosleđivanjem niza veze u konstruktor
SqlConnection cn = new SqlConnection(
"Data Source=(local);Initial Catalog=pubs;User ID=sa;Password=");
cn.Open(); // Otvori vezu

Direktiva using koja se nalazi u prvom redu nije obavezna, ali ako je upotrebite, nećete morati
da kucate dodatne redove. U slučaju da je ne upotrebite, morali biste da napišete
System.Data.SqlClient.SqlConnection umesto jednostavnije varijante
SqlConnection. Primetićete da pri kodiranju u C# nije potrebno dodavanje reference
datoteci System.Data.dll (gde se nalaze dobavljači SqlClient i OleDb), čak i kada se
upotrebljava kompajler sa komandnom linijom. Međutim, kada su u pitanju drugi jezici,
potrebno je da dodate reference, osim u slučaju kada koristite Visual Studio.NET (program
automatski dodaje referencu).
Naš sledeći zadatak je da formiramo objekat SqlConnection – SqlClient implementacija
interfejsa IDbConnection. Prosledićemo informacije veze u konstruktor za pomenuti objekat
iako možemo da formiramo objekat upotrebom podrazumevanog (bez parametara)
konstruktora, a zatim podesimo njegovo svojstvo ConnectionString. Niz veze je skoro
identičan ADO nizu veze – jedina razlika je u tome što, u ADO.NET-u, ne moramo da
specifikujemo dobavljača koji koristimo. To smo već uradili formiranjem SqlConnection
objekta (umesto OleDbConnection objekta). Na kraju, pozivamo metod Open kako bismo
otvorili vezu. Za razliku od metoda Open, objekta ADO Connection, nismo u mogućnosti da
prosledimo niz veze kao parametar u pomenuti metod, pa zato moramo specifikovati
informacije veze pre samog njenog otvaranja.

19
Poglavlje 1

OleDb dobavljač
Ako ne koristite SQL Server 7.0 ili neku od ostalih verzija, naša preporuka je da koristite OleDb
dobavljač. Međutim, postoji nekoliko izuzetaka; ako vaš izvor podataka sadrži ODBC upravljački
program, ali ne i OLE DB dobavljač, potrebno je da upotrebite Odbc.NET dobavljač. Podrška za
MSDASQL (OLE DB dobavljač za ODBC upravljački program) je povučena iz OleDb dobavljača
negde između Beta 1 i Beta 2 verzije .NET razvojnog okruženja i, samim tim, ne postoji
odgovarajuća alternativa za ovu vrstu podrške. Ovo je urađeno da bi se sprečila upotreba ODBC
naziva izvora podataka (engl. Data Source Names) – DSN – zajedno sa ADO.NET-om, osim u
slučajevima gde je ODBC zaista neophodan. Čak i u programu ADO, upotreba DSN-a
podrazumevala je određeno smanjenje kvaliteta performansi (pogotovo u slučaju kada je OLE
DB dobavljač bio dostupan). Međutim, dodatni sloj ne bi se tolerisao u okvirima .NET okruženja.
Pomislite na datu arhitekturu: ADO.NET – COM interoperabilnost – (opcionalno) OLE DB
usluge – OLE DB dobavljač – ODBC upravljački program – izvor podataka!
Već smo pominjali sledeću situaciju u kojoj OleDb dobavljač nije od velike pomoći. Ako je
potrebno da pristupite izvoru podataka koristeći Exchange 2000 ili Internet Publishing Provider
(IPP), moramo vas obavestiti da ne postoji alternativa za COM interoperabilnost i zastareli
program ADO. Naime, OleDb dobavljač ne podržava interfejse IRecord i IStream koje
koriste pomenuti dobavljači.
OleDb dobavljač se po mnogim karakteristikama ponaša kao i tradicionalni ADO. Naime, on
predstavlja samo .NET omotač oko OLE DB-a (osim što su sada Ole Db dobavljači usluga
prilično zastareli, s obzirom na to da se pomenuta funkcija nalazi u programu ADO.NET). Osim
što moramo specifikovati da ćemo upotrebiti OleDb.NET dobavljač (formiranjem objekta
OleDbConnection), takođe je potrebno da specifikujemo OLE DB dobavljača podataka koji
želimo da koristimo pri uspostavljanju veze iz OLE DB-a sa izvorom podataka. Ovaj proces se
može obaviti na isti način kao i u programu ADO – uključivanjem svojstva Provider u niz
veze ili podešavanjem svojstva Provider objekta OleDbConnection.
OleDb dobavljač nalazi se u datoteci System.Data.dll (kao i SqlClient) i sadržan je u
.NET razvojnom okruženju. Klase koje sačinjavaju dobavljača nalaze se u imenovanom prostoru
System.Data.OleDb i sve imaju prefiks ''OleDb'' (OleDbConnection, OleDbCommand
itd.) Sledeće je prikazivanje pomenutog procesa, i to otvaranjem veze sa Access bazom
podataka Northwind (u ovom slučaju, Nwind.mdb):

using System.Data.OleDb; // The OleDb provider namespace

// Formiraj objekat OleDbConnection


OleDbConnection cn = new OleDbConnection(
@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb");
cn.Open(); // Open the connection

Znak @ koji se nalazi ispred niza veze koristi se u programu C# kako bi se ukazalo na ''doslovan'' niz,
što znači da će svi iskočni znaci biti ignorisani. Ova opcija je veoma korisna kada su u pitanju putanje
datoteka (za izbegavanje znaka obrnuta kosa crta).
Ovaj primer se ne razlikuje mnogo od prethodnog, osim što je, kao što smo napomenuli,
potrebno da uključimo i svojstvo Provider u niz veze. Stoga, iako smo upotrebili različite
objekte, u suštini smo promenili samo tri stvari:
❐ direktivu using na početku koda;
❐ niz veze;
❐ prefiks ''OleDb'' svaki put kada formiramo objekte specifične za dobavljača.

Ovo je takođe prirodan izbor dobavljača koji se upotrebljava za povezivanje sa Oracle bazom
podataka:

20
Pristup podacima i .NET okruženje

using System.Data.OleDb;
OleDbConnection cn = new OleDbConnection("Provider=MSDAORA;" +
"Data Source=orcl.julian_new.wrox.com;User ID=scott;" +
"Password=tiger");
cn.Open();

Kao i u programu ADO, i ovde prosleđujemo naziv Oracle dobavljača OLE DB (MSDAORA),
naziv servisa (ovde je orcl.julian_new.wrox.com) kao izvor podataka, a zatim i šemu u
Oracle bazi podataka kao UserID (u ovom slučaju, scott).

Odbc dobavljač
Za razliku od ostala dva .NET dobavljača, Odbc dobavljač nije sadržan u .NET razvojnom
okruženju. Trenutnu beta verziju možete preuzeti kao .exe datoteku od 503 KB sa MSDN Web
lokacije http://www.microsoft.com/downloads/release.asp?ReleaseID=31125. Kada prenesete
pomenutu datoteku, aktivirajte je da biste instalirali klase (ovaj program će instalirati skup u
globalnu keš memoriju skupa (engl. Global Assembly Cache) tako da će klase automatski biti
globalno dostupne na lokalnoj mašini. Međutim, potrebno je da vašim projektima dodate i
referencu za skup (System.Data.Odbc.dll) kako biste koristili dobavljač.
Odbc dobavljač bi trebalo upotrebljavati svaki put kada vam je potreban pristup izvoru
podataka bez OLE DB dobavljača (poput PostgreSQL ili starijih baza podataka kao što su
Paradox ili dBase) ili u slučaju kada želite da upotrebite ODBC upravljački program za funkciju
koja nije dostupna u OLE DB dobavljaču. Odbc dobavljač je po strukturi veoma sličan OleDb
dobavljaču – ponaša se kao .NET omotač oko ODBC API i pritom dozvoljava programu
ADO.NET da pristupi izvoru podataka kroz ODBC upravljački program.
Klase ODBC dobavljača nalaze se u imenovanom prostoru System.Data.Odbc i počinju
prefiksom ''Odbc''. U primeru koji sledi povezaćemo se sa bazom podataka MySQL na sledeći
način (ovde se povezujemo sa kopijom Access baze podataka Northwind koju smo uvezli u
bazu podataka MySQL):

using System.Data.OleDb; // imenovani prostor dobavljača OleDb

// Formiraj objekat OleDbConnection


OleDbConnection cn = new OleDbConnection(
"DRIVER={mySQL};SERVER=JULIAN;DATABASE=Northwind;UID=root;PWD=");
cn.Open(); //Otvori vezu

Jedina razlika u odnosu na prethodni primer je u tome što ovde upotrebljavamo ODBC niz veze
umesto OLE DB niza veze (kao što bismo uradili pri povezivanju sa ODBC izvorom podataka iz
programa ADO). Shodno tome, ovo može biti prethodno konfigurisana veza u obliku DSN-a
(Data Source Name) ili može biti potpuni niz veze (kao u prethodnom primeru) koja specifikuje
ODBC upravljački program koji se upotrebljava, naziv servera baze podataka i bazu podataka
na serveru, kao i korisnički ID (UID) i lozinku (PWD).

Komponenta DataSet
Još jedna u nizu važnih komponenti ADO.NET-a jeste DataSet. Pomenuta komponenta u suštini
odgovara skupu zapisa (engl. recordset) programa ADO, ali se ipak razlikuje na dva veoma bitna
načina. Naime, objekat DataSet je uvek isključen, a posledica toga je da ne proverava odakle
dolaze podaci. Objekat DataSet može se koristiti na potpuno isti način za manipulisanje
podacima iz tradicionalnog izvora podataka ili iz XML dokumenta. Da bismo povezali objekat
DataSet sa izvorom podataka, potrebno je da upotrebimo objekat DataAdapter kao
posrednika između DataSet i .NET dobavljača podataka:

21
Poglavlje 1

.NET dobavljač podataka

Veza Izvor
podataka

Komanda

DataReader

DataSet DataAdapter
XML
dokument

U sledećem primeru, popularisaćemo objekat DataSet sa podacima iz tabele Employee u bazi


podataka Northwind:

// Otvori vezu
OleDbConnection cn = new OleDbConnection(
@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb");
cn.Open();

// Napravi novi objekat DataAdapter i prosledi komandu SELECT


OleDbDataAdapter da = new OleDbDataAdapter(
"SELECT EmployeeID, FirstName, LastName FROM Employees", cn);

// Napravi novi DataSet


DataSet ds = new DataSet();

// Popuni DataSet
da.Fill(ds, "Employees");

// Zatvori vezu; sada imamo podatke


cn.Close();

Po otvaranju veze, postoje tri postupka u procesu popularisanja objekta DataSet:


❐ Formiranje novog primerka objekta DataSet. Neposredno pre popunjavanja objekta
DataSet, potrebno je da se specifikuju informacija o vezi i podaci koje želimo da
upotrebimo za popunjavanje objekta. Postoji mnogo načina na koje se pomenuti proces
može obaviti i jedan od najjednostavnijih jeste da prosledimo tekst komande za SQL
upit zajedno sa nizom veze ili otvorenom vezom u DataAdapter konstruktor (kao što
je i učinjeno u prethodno navedenom primeru).
❐ Stvaranje novog objekta DataSet.
❐ Pozivanje DataAdapter metode Fill. Zapravo, ovde je potrebno proslediti objekat
DataSet koji želimo da popularišemo kao parametar za pomenutu metodu, kao i
naziv tabele u okviru objekta DataSet koji želimo da popunimo. Ako pozovemo
metodu Fill nasuprot zatvorene veze, veza će se automatski otvoriti, a zatim ponovo
zatvoriti kada se objekat DataSet popuni.

22
Pristup podacima i .NET okruženje

Klasa DataTable
Ovaj poslednji parametar nam ukazuje na jednu od najbitnijih razlika između objekta DataSet
i ADO skupa zapisa, a to je da objekat DataSet može sadržati nekoliko tabela podataka. Iako
je nešto slično postojalo u programu ADO u okviru opcije uređivanja podataka, ipak je objekat
DataSet učinio korak više – njegove tabele mogu biti uzete iz različitih izvora podataka.
Takođe, rešen je problem vezan za upotrebu užasne sintakse SHAPE. ADO.NET poseduje klasu
DataTable koja predstavlja jednu tabelu u okviru objekta DataSet. Objekat DataSet sadrži
svojstvo Tables koje vraća kolekciju pomenutih objekata (DataTableCollection). Klasa
DataTable prikazuje podatke u uobičajenom tabelarnom formatu i poseduje kolekciju
objekata DataColumn i DataRow koje predstavljaju svaku kolonu i svaki red u tabeli:

DataSet

DataTableCollection

DataTable

DataColumnCollection DataColumn

DataRowCollection DataRow

Iako klasa DataColumn odgovara ADO objektu Field, program ADO nije posedovao
objekat koji je prikazivao jedan red podataka, te stoga s pravom možemo reći da klasa DataRow
predstavlja veliku prednost!
Ako želite da pristupite podacima u klasi DataTable, potrebno je da prvo pristupite
odgovarajućem objektu DataRow, a zatim i indeksirate objekat kako biste dobili podatke za
željenu kolonu. Indeks može biti numerički indeks kolone (0 za prvu kolonu itd.) ili naziv
kolone. U sledećem primeru ponovićemo pregledanje kolekcije DataColumnCollection
kako bismo izdvojili nazive kolone u prvoj tabeli objekta DataSet. Zatim ćemo ponoviti
izvršavanje iskaza u svakom redu i svakoj koloni za trenutni red i prikazati podatke u prostoj
tabeli komandne linije:

// Prikaži nazive kolone


foreach (DataColumn dc in ds.Tables[0].Columns)
Console.Write("{0,15}", dc.ColumnName);

// Dodaj novu liniju posle zaglavlja kolone


Console.Write("\n");

// Prikaži podatke za svaki red


// Izvrši petlju kroz redo
za svaku (DataColumn dc in ds.Tables[0].Cloumns)
{
// Zatim izvrši petlju kroz kolone za trenutni red

23
Poglavlje 1

za (int i=0; i<ds.Tables[0].Columns.Count; i++)


Console.Write("{0,15}", dr[i]);

// Dodaj linijski prekid posle svakog reda


Console.Write("\n");
}

Ako ste izvršili pomenuti deo kôda upotrebom objekta DataSet koji smo prethodno
popularisali, videćete sledeći prikaz na ekranu:

Ažuriranje izvora podataka


Ako zapravo želimo samo da ponavljamo izvršavanje iskaza kroz podatke, onda bismo mnogo
bolje prošli koristeći objekat DataReader. Objekat DataSet bi trebalo upotrebljavati samo
kada je potrebna manipulacija podacima na klijentu. Međutim, upravo ovde nastaje problem.
Kao što smo prethodno napomenuli, ako pokušamo da izvršimo promene na objektu DataSet,
a zatim pozovemo pridružen DataAdapter metod Update, dobićemo poruku o grešci koja
nas obaveštava da odgovarajući komandni objekat nije podešen.
Najjednostavniji način da napravimo komandni objekat jeste upotreba objekta
CommandBuilder našeg dobavljača. Da bismo videli kako sve ovo funkcioniše u praksi,
potrebno je da promenimo vrednost poslednjeg zapisa FirstName iz ''Anne'' u ''Anna'':

// Promeni vrednost u objektu DataSet


ds.Tables[0].Rows[8]["FirstName"] = "Anna";

// Izvrši ponovno povezivanje sa izvorom podataka


cn.Open();

// Napravi objekat OleDbCommandBuilder i prosledi objekat DataAdapter


// u konstruktor
OleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(da);

// Prikaži UpdateCommand
Console.WriteLine(cmdBuilder.GetUpdateCommand().CommandText);

// Ažuriraj izvor podataka

24
Pristup podacima i .NET okruženje

da.Update(ds.Tables[0]);

// Zatvori vezu
cn.Close();

Složićete se da je prethodni proces prilično jasan. Prosledili smo naš objekat DataAdapter u
konstruktor OleDbCommandBuilder, čime je došlo do automatskog povezivanja sa našim
izvorom podataka. Takođe smo izbegli grešku InvalidOperationException formiranjem
primerka CommandBuilder za objekat DataAdapter, čime su automatski izgrađene
komande. Ako želimo da izdvojimo tekst generisanih komandi, to možemo učiniti pozivanjem
CommandBuilder metoda GetUpdateCommand, GetInsertCommand itd. S obzirom na to
da ažuriramo samo zapis (umesto brisanja ili ubacivanja zapisa), prikazaćemo jedino komandu
UPDATE. Da bismo obavili prethodno pomenuti proces, potrebno je da pozovemo metodu
GetUpdateCommand koja vraća komandni objekat (u našem slučaju, objekat OleDbCommand
iz razloga što koristimo OleDb dobavljač). Shodno tome, CommandText će glasiti ovako:
UPDATE 'Employees' SET 'FirstName' = ? , 'LastName' = ? WHERE (
'EmployeeID' = ? AND 'FirstName' = ? AND 'LastName' = ? )

Sada možemo da pozovemo DataAdapter metod Update. U ovom procesu postoji problem
preopterećivanja, te se stoga objekat DataSet može uzeti kao parametar (sa ili bez naziva
tabele u okviru objekta DataSet), kao, uostalom i klasa DataTable ili pak niz objekata
DataRow. Naime, ovde samo prosleđujemo klasu DataTable koju želimo da ažuriramo. Na
kraju zatvaramo vezu.

ADO.NET i XML
Jedna od najimpresivnijih novih karakteristika ADO.NET-a jeste ugrađena podrška za XML.
Naime, XML sada predstavlja standardni postojani format za ADO.NET DataSets. Iako smo
bili u mogućnosti da sačuvamo skupove zapisa u XML formatu počev od verzije 2.1 programa
ADO, podrazumevani format i dalje je bio format Advanced Data TableGram (ADTG), a XML
podrška i dalje ograničena. Na primer, nismo mogli da učitamo proizvoljan XML dokument u
ADO skup zapisa jer je dokument morao da bude u tačno određenom formatu.
XML podrška u ADO.NET-u je mnogo kompletnija. XML predstavlja apsolutno integralni deo
ADO.NET-a, a ne samo jednostavan programski dodatak. XML je format koji se koristi za
serijalizovanje i transportovanje objekata DataSets. Serijalizacija objekta DataSet kao XML
dokumenta (u datoteku, niz ili objekat TextWriter) predstavlja prilično trivijalan proces:

// Sačuvaj objekat DataSet kao XML datoteku


ds.WriteXml(@"C:\CSharp\Employees.xml");

Format generisanog XML dokumenta je čitljiviji od njegovog ADO ekvivalenta – kolone su


prikazane po elementima a ne po atributima, i ne postoje nepotrebni XML prostori imena:
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<Employees>
<EmployeeID>1</EmployeeID>
<FirstName>Nancy</FirstName>
<LastName>Davolio</LastName>
</Employees>
<Employees>

25
Poglavlje 1

<EmployeeID>2</EmployeeID>
<FirstName>Andrew</FirstName>
<LastName>Fuller</LastName>
</Employees>
<!-- and so on... -->
</NewDataSet>

Takođe smo u mogućnosti da učitamo dobro formiran XML dokument u objekat DataSet bez
potrebe korišćenja prethodno definisanih struktura (iako postoji mogućnost da izgubimo sadržaj
ako struktura dokumenta nije tabelarna). Na primer (potrebno je da dodate System.IO;
direktivu da biste pokrenuli svoj kôd za ovaj primer):

// Uskladišti XML dokument u nizu


string xmlDoc = @"<?xml version='1.0'?>
<books>
<book>
<title>Pro ADO.NET</title>
<publisher>Wrox Press</publisher>
</book>
</books>";

// Učitaj ovo u StringReader


StringReader sr = new StringReader(xmlDoc);

// Napravi novi objekat DataSet i čitaj u XML


DataSet ds = new DataSet();
ds.ReadXml(sr);

// Prikaži nazive kolone i podatke reda u obliku tabele


foreach (DataColumn dc in ds.Tables[0].Columns)
{
Console.Write("{0,-15}", dc.ColumnName);
}
Console.Write("\n");
foreach (DataRow dr in ds.Tables[0].Rows)
{
Console.WriteLine("{0,-15}{1,-15}", dr[0], dr[1]);
}

Ovde smo napravili veoma jednostavan XML dokument i uskladištili ga u nizu. Upotrebićemo
DataSet metod ReadXml za učitavanje XML dokumenta u objekat DataSet. Ovde se XML
dokument u obliku niza ne prihvata kao parametar, ali se zato prihvata klasa StringReader
(ova klasa nalazi se u imenovanom prostoru System.IO), te stoga pravimo novu klasu
StringReader iz našeg niza i prosleđujemo je metodu ReadXml. Na kraju ćemo prikazati
objekat DataSet u tabelarnom formatu.
Kada izvršite ovaj proces, trebalo bi da vidite sledeće:

26
Pristup podacima i .NET okruženje

Definisani DataSets
S obzirom na to da se XML može pronaći u svim delovima .NET razvojnog okruženja, postoji
čitav niz alatki koje su dostupne za rad sa XML dokumentima i šemama. Najimpresivnija među
njima jeste xsd.exe koja može da preuzme XML šemu za definisanje šema (skr. XSD) kao
ulazni podatak i iz nje generiše strogo definisani tip objekta DataSet. Nije potrebno naglasiti da
postoji mogućnost upotrebe objekta DataSet za automatsko generisanje XSD šema.
Na primer, u mogućnosti smo da generišemo XSD šemu za tabelu Employees u bazi podataka
Northwind, i to upotrebom sledećeg koda:

OleDbConnection cn = new OleDbConnection(


@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb");
cn.Open();
OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM Employees", cn);
DataSet ds = new DataSet();
da.Fill(ds, "Employees");
cn.Close();
ds.WriteXmlSchema("Employees.xsd");

Zatim možemo da aktiviramo xsd.exe za pomenutu šemu, i to upotrebom opcije /d kako


bismo ukazali na to da želimo da generišemo izvorni kôd za strogo definisani tip objekta
DataSet. Da bismo upotrebili xsd.exe iz komandnog odzivnika i prebacili se u direktorijum
koji sadrži XSD šemu, a zatim i uneli pomenutu komandu (pod pretpostavkom da smo
direktorijum C:\Program Files\Microsoft.NET\FrameworkSDK\Bin dodali
promenljivoj okruženja PATH:

xsd Employees.xsd /d

Ovaj proces generiše datoteku C# pod nazivom Employees.cs koja definiše klasu
NewDataSet izvedenu iz DataSet zajedno sa strogo definisanim tipom klasa DataTable i
DataRow. S obzirom na to da je klasa NewDataSet izvedena iz DataSet, ona poseduje
funkciju normalne klase DataSet. Međutim, klasa DataRow izlaže svaku kolonu u redu kao
svojstvo odgovarajućeg tipa podataka (na primer, kolona varchar biće izložena u obliku niza).
Da biste videli klasu DataSet u akciji, potrebno je da kompajlirate sledeću C# datoteku u DLL:

csc /t:library Employees.cs

Sada možemo da upotrebimo pomenuti skup (Employee.dll) za stvaranje strogo definisanog


tipa DataSet. Naime, klasa NewDataSet poseduje ugneždenu klasu EmployeesRow (koja je
izvedena iz klase DataRow). Pomenuta klasa sadrži javno svojstvo za vraćanje vrednosti svake
kolone u tabeli. To znači da smo u mogućnosti da umesto unošenja iskaza dr["FirstName"],
otkucamo samo dr.FirstName:

27
Poglavlje 1

// Otvori i populariši DataSet kao normalnu...


OleDbConnection cn = new OleDbConnection(
@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb");
cn.Open();
OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM Employees", cn);

// Jedina razlika je u tome što koristimo klasu NewDataSet umesto samo


DataSet
NewDataSet ds = new NewDataSet();
da.Fill(ds, "Employees");
cn.Close();

// Prikaži podatke za svaki red


foreach (NewDataSet.EmployeesRow dr in ds.Employees.Rows)
{
Console.WriteLine("{0,15}{1,15}", dr.FirstName, dr.LastName);
}

Sigurno smatrate da je ovo beznačajna razlika i da nije vredna truda oko generisanja nove klase.
Ako koristite Notepad, u potpunosti ste u pravu. Međutim, ako koristite moćni IDE sa modulom
IntelliSense i automatsko upotpunjenje poput programa Visual Studio .NET, nazivi kolona biće
prikazani kao svojstva reda:

Drugim rečima, zaboravite na greške zbog nepravilno otkucanog ili pogrešno zapamćenog
naziva kolone u bazi podataka! Još jedna prednost je u tome što je poboljšana čitljivost kôda i
smanjena mogućnost greške jer se strogo definišu tipovi u vreme kompajliranja. Mogućnost
unošenja vrednosti pogrešnog tipa u kolonu svedena je na minimum.

ADO.NET i ADO 2.6


S obzirom na to da je program ADO.NET na neki način direktna zamena za verziju ADO 2.6 u
okviru .NET okruženja, kao i to da je većina projektanata koji ga koriste već upoznata sa
tradicionalnim pristupom programu ADO, potrebno je objasniti razliku između dve pomenute
tehnologije.
Postoje dve glavne razlike između ADO.NET-a i ADO 2.6. Prvenstveno, ADO.NET je
namenski opremljen za upotrebu u dva različita okruženja: okruženju isključenih skupova zapisa
i okruženju povezanog pristupa izvorima podataka samo za čitanje i prosleđivanje. Kao drugo,
ADO.NET ne predstavlja jedan objedinjeni model objekta za projektante nezavisno od izvora
podataka. Naprotiv, on koristi klase specifikovane za dobavljače koje su implementirane samim
.NET dobavljačem.

28
Pristup podacima i .NET okruženje

Pristup isključenim podacima


ADO.NET je optimizovan za isključeni skup rezultata (engl. resultset). Kao što smo videli,
pomenuti skup je implementiran klasom DataSet koja je potpuno fleksibilna i koja se može
ažurirati, međutim, ne može da zadrži trajnu vezu sa samim izvorom podataka. Zapravo,
DataSet je na određeni način veoma sličan isključenom skupu zapisa u programu ADO, s tim
što DataSet vrši isključivanje automatski, dok su ranije projektanti morali sami da isključuju
skup zapisa iz izvora podataka i zatvaraju veze (izvor konfuzije i velikih grešaka). Na primer,
tipična metoda za vraćanje isključenog skupa zapisa može sadržati ADO 2.6 kôd poput sledećeg
(u programu VB 6):

Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset

Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=SQLOLEDB;Data Source=JULIAN;Initial Catalog=pubs;" & _
"User ID=sa;Password="
Set rs = CreateObject("ADODB.Recordset")
rs.LockType = adLockBatchOptimistic ' Specify the lock type
rs.CursorLocation = adUseClient ' Specify that we're using
' OLE DB's Client Cursor Engine
rs.CursorType = adOpenStatic ' Specify the cursor type
rs.Open "SELECT * FROM authors", objConn ' Open the recordset
Set objRec.ActiveConnection = Nothing ' Disconnect the recordset
objConn.Close ' Close the connection
Set objConn = Nothing

Svaki put kada u ADO.NET-u napravite klasu DataSet, ona će automatski biti isključena.
Uostalom, videli smo da je potrebno upotrebiti klasu DataAdapter kao posrednika između
DataSet i dobavljača podataka. Na primer:

OleDbConnection cn = new
OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:\NWind.mdb");
cn.Open();
OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM Employees", cn);
DataSet ds = new DataSet();
da.Fill(ds, "Employees");
cn.Close();

Ovaj pristup je mnogo transparentniji i manje podložan greškama, a ceo proces je mnogo kraći.
Samim tim nije potrebno da definišemo dodatnu informaciju poput CursorType zato što je
isključeni DataSet po definiciji statički. (Takođe ćemo dobiti statički kursor u programu ADO,
nezavisno od tipa koji smo definisali, pre svega zbog toga što Client Cursor Engine [CCE]
podržava samo statičke kursore. Stoga ne možemo definisati tip kursora za DataSet.)

29
Poglavlje 1

Pristup samo za čitanje, samo za prosleđivanje


Sledeća situacija za koju je ADO.NET optimizovan jeste kada želimo da ponovimo izvršavanje
jedne funkcije kroz svaki red ponaosob u skupu rezultata. U ovom scenariju nećemo menjati
podatke, već ćemo se lagano kretati kroz skup rezultata kako ne bismo čekali učitavanje
celokupnog skupa rezultata pre nego što započnemo proces ponovnog izvršavanja funkcije. Za
ovakav tip pristupa logično je da je potrebno držati vezu sa izvorom podataka otvorenom tokom
procesa ponavljanja u podacima. Upravo je zbog ovakvih situacija i stvorena klasa DataReader.
Samim tim što imamo klasu koja je specijalno napravljena za određeni zadatak, u mogućnosti
smo da pišemo jednostavnije kodove. Na primer, ako želimo da obavimo proces ponavljanja
kroz ADO skup zapisa, možemo upotrebiti sledeći VB 6 kôd:

Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset

Set cn = New ADODB.Connection


cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb"

Set rs = New ADODB.Recordset


rs.Open "SELECT * FROM Employees", cn, adOpenForwardOnly, adLockReadOnly

While Not rs.EOF


MsgBox rs!FirstName & " " & rs!LastName
rs.MoveNext
Wend

rs.Close
cn.Close

ADO.NET ekvivalenat bio bi sledeći:

OleDbConnection cn = new
OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:\NWind.mdb");
cn.Open();
OleDbCommand cmd = new OleDbCommand("SELECT * FROM Employees", cn);
OleDbDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (dr.Read())
{
Console.WriteLine (dr["FirstName"] + " " + dr["LastName"]);
}
dr.Close();

Primetićete da specifikovanje kursora ili tipova blokade nije potrebno. Već znamo da nam je
potreban kursor samo za prosleđivanje i samo za čitanje, te stoga nema potrebe da pamtimo
veliki broj parametara pri otvaranju klase DataReader.
Još jedna od prednosti je u tome što metod Read automatski prelazi u sledeći red. U programu
ADO bilo je lako napisati kôd koji bi stvarao beskonačnu petlju, i to samo zbog toga što bismo
zaboravili da dodamo poziv metodu MoveNext. Međutim, sada je situacija potpuno drugačija –
DataReader.Read pomera pokazivač unutrašnjeg reda klase DataReader za jedan red čime
podaci tog reda postaju dostupni. Sam metod vraća Bulovu vrednost „tačno” dok su podaci i
dalje u procesu čitanja, a vrednost „netačno” kada stigne do kraja datoteke. Stoga, umesto da
zasebno pozivamo metod MoveNext i EOF, to možemo učiniti odjednom. Rezultat – nema
beskonačnih petlji!
Jedino treba zapamtiti da klasa DataReader polazi od početne lokacije datoteke, te je stoga
potrebno pozvati metod Read najmanje jednom pre nego što nam se omogući pristup podacima.

30
Pristup podacima i .NET okruženje

Suština svih navedenih promena je sledeća: tamo gde smo imali jedan objekat (RecordSet)
koji je obavljao sav posao u ADO 2.6, sada imamo specijalizovane klase za dve najčešće
situacije. Zato možemo poništiti veliki broj nejasnih (i često nepravilno protumačenih)
parametara koje smo imali pri otvaranju skupa zapisa ili izvršavanju komande, a da pritom
budemo sigurni u to da dobijamo tačno određeni i željeni tip objekta. Međutim, u slučaju da
želimo nestandardan način pristupa našim podacima – na primer, povezani skup rezultata kojim
možemo da upravljamo i koji možemo da ažuriramo – nećemo moći da se oslonimo na ovaj
program. Naime, ADO.NET ne obezbeđuje ovakvu vrstu podrške.
Takođe je važno zapamtiti da je celokupno .NET razvojno okruženje izrađeno tako što je pažnja
bila podjednako podeljena između raspodeljene obrade, preko Interneta i izlaganja funkcija
putem Web servisa. Međutim, ako ostavite otvorene veze sa bazom podataka, postoji velika
mogućnost da će to ozbiljno uticati na podesivost aplikacije, što se kosi sa prvenstvenim ciljem
.NET okruženja, a to je, upravo, sprečavanje takvih pojava. Ako vam je zaista potrebna
pomenuta funkcija, preporučujemo vam upotrebu klasične verzije programa ADO.

Naime, i dalje ste u mogućnosti da ažurirate izvor podataka, i to izvršavanjem komandi upotrebom
komandnih klasa. Ako vam je potreban povezani pristup, kombinovanje pomenutih klasa sa klasom
DataReader predstavlja najefikasniju tehniku. Međutim, takođe će vam biti potreban namenski
kôd za simuliranje originalnog povezanog skupa zapisa koji se može ažurirati.

Specifikovane klase dobavljača


Sledeća razlika između programa ADO.NET i ADO jeste da u prvom postoje specifični skupovi
klasa za svakog dobavljača. U programu ADO upotrebljavali smo objekte Connection,
Command i RecordSet nezavisno od izvora podataka kojem smo pristupali. Stoga, ako bismo
poželeli da promenimo sistem baze podataka koji smo do tada koristili (na primer, ako smo
umesto programa Access počeli da koristimo SQL Server), sve što je bilo potrebno uraditi jeste
promeniti niz veze. Međutim, sada je potrebno napraviti specifični objekat SqlConnection,
OleDbConnection itd. u zavisnosti od dobavljača podataka.
Pomenuta situacija veoma je slična onoj u OLE DB, gde je naš prvi zadatak bio formiranje
primerka OLE DB dobavljača upotrebom CoCreateInstance:

CoInitialize(NULL);
CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER,
IID_IDBInitialize,
(void **) &pIDBInitialize);

Međutim, po izvršavanju ovog zadatka, obično smo upotrebljavali standardne interfejse za


izvršavanje komandi u bazi podataka. U ADO.NET-u, razlike između specifikovanih klasa
dobavljača su veće, s obzirom na to da imamo odvojene klase za objekte Command, DataReader
i DataAdapter. Naime, ako želimo da promenimo RDBMS koji se upotrebljava u našem kodu,
potrebno je promeniti i imenovane prostore koje smo uvezli u projekat, kao i kôd, kako bismo
deklarisali i formirali primerak svih korišćenih specifikovanih klasa dobavljača i niz veze.
Ovaj proces nam se može učiniti kao trivijalni zadatak traženja i zamenjivanja, međutim, upravo
ovde leži jedna od najznačajnijih razlika između tehnike korišćene u ADO.NET-u i OLE DB-u.
Kao što smo videli, uobičajeni metodi i svojstva za .NET klase dobavljača definisani su u
interfejsima u imenovanom prostoru System.Data. Na primer, metod ExecuteReader
definisan je u interfejsu IDbCommand. Do sada, čitav proces je prilično sličan procesu koji se
obavlja u OLE DB-u, gde je (na primer) metod Execute definisan u interfejsu IDbCommand.
Međutim, kada je u pitanju OLE DB, mi direktno radimo sa interfejsima; u ADO.NET-u radimo
sa objektima koji implementiraju pomenute interfejse. Naime, programer u ADO.NET-u ne mora
da zna da li su pozvani metodi i svojstva definisani u interfejsu ili su specifični za samog dobavljača.

31
Poglavlje 1

Da bismo bliže objasnili navedenu razliku, razmotrićemo definiciju interfejsa za


IDbConnection. Ovde su definisana samo četiri svojstva i pet metoda:
public interface System.Data.IDbConnection
{
// Properties
string ConnectionString { get; set; }
int ConnectionTimeout { get; }
string Database { get; }
ConnectionState State { get; }

// Methods
System.Data.IDbTransaction BeginTransaction();
System.Data.IDbTransaction BeginTransaction(
System.Data.IsolationLevel il);
void ChangeDatabase(string databaseName);
void Close();
System.Data.IDbCommand CreateCommand();
void Open();
} // end of System.Data.IDbConnection

Sada ćemo videti kako se prethodno navedeni proces implementira pomoću objekta
OleDbConnection. Da bi se uštedeo prostor, neki od članova su izostavljeni iz sledeće
definicije. Svi članovi koji su definisani putem interfejsa IDbConnection takođe su i
implementirani; označeni članovi predstavljaju članove koji nisu implementacije članova
IDbConnection:
public sealed class System.Data.OleDb.OleDbConnection :
System.ComponentModel.Component,
System.ComponentModel.IComponent,
IDisposable,
ICloneable,
System.Data.IDbConnection
{
// Properties
public string ConnectionString { virtual get; virtual set; }
public int ConnectionTimeout { virtual get; }
public string Database { virtual get; }
public string DataSource { get; }
public string Provider { get; }
public string ServerVersion { get; }
public ConnectionState State { virtual get; }

// Events
public event OleDbInfoMessageEventHandler InfoMessage;
public event StateChangeEventHandler StateChange;

// Methods
public System.Data.OleDb.OleDbTransaction BeginTransaction();
public System.Data.OleDb.OleDbTransaction
BeginTransaction(System.Data.IsolationLevel isolationLevel);
public virtual void ChangeDatabase(string value);
public virtual void Close();
public System.Data.OleDb.OleDbCommand CreateCommand();
public virtual System.Runtime.Remoting.ObjRef CreateObjRef(Type
requestedType);
public System.Data.DataTable GetOleDbSchemaTable(Guid schema, object[]

32
Pristup podacima i .NET okruženje

restrictions);
public virtual void Open();
public static void ReleaseObjectPool();
} // end of System.Data.OleDb.OleDbConnection

Primetićete da je klasa OleDbConnection (i ostale klase veze) izvedena iz klase Component i


da implementira veliki broj drugih interfejsa pored IDbConnection. Implementacije članova
ovih interfejsa su izostavljene. Jer, čak i kada ignorišemo ove članove (koji nisu direktno u vezi sa
funkcijama klase), postoje još tri dodatna svojstva, dva događaja i tri metoda. Neki od njih su
implementirani putem drugih dobavljača (poput događaja StateChange), dok neki nisu (na
primer, metod GetOleDbSchemaTable nema svog ekvivalenta u dobavljačima SqlClient
ili Odbc). Osim proveravanja dokumentacije, ne postoji način na koji možete da budete sigurni
da li je metod ili svojstvo zajedničko za sve dobavljače.
Ova mogućnost predstavlja kako veliku prednost, tako i veliki hendikep. S jedne strane, ona
obezbeđuje projektantima .NET dobavljača podataka enormnu količinu fleksibilnosti. Pored
mogućnosti dodavanja dodatnih članova, postoji i mogućnost da individualni dobavljači dodaju
dodatne klase i druge tipove (na primer, dobavljač SqlClient sadrži klasu SqlDebugging
koja nema svog ekvivalenta u drugim dobavljačima). Ovim je projektantima dobavljača
omogućeno da se pojedinim izvorima podataka pristupa na pogodnije načine, čime je ujedno i
obezbeđeno da se dobavljači projektuju za izvore podataka koji ne odgovaraju postojećem
modelu (na veoma sličan način kao što su interfejsi OLE DB 2.5 omogućili razvoj OLE DB
dobavljača MSDAIPP i ExOLEDB).
Međutim, loša strana je ta što ukoliko svi projektanti dobavljača krenu sopstvenim putem i
razviju veliki broj proširenja za dobavljače, projektanti koji koriste ADO.NET bi morali da budu
upoznati sa svakim dobavljačem koji im je potreban. Samim tim, sve ovo predstavlja veliki
korak unazad za univerzalni pristup podacima (engl. Universal Data Access) i delimičan povratak
API specifikovanim izvorima podataka. Međutim, revizija funkcije glavne memorije u
ADO.NET interfejsima osiguraće da razlike između dobavljača budu što manje.
Vredno je istaći da ukoliko želite da napišete kôd koji je nezavisan od dobavljača, možete
direktno primeniti kôd u interfejsima:

// Formiraj objekat veze kao i obično


OleDbConnection oleDbConn = new OleDbConnection(
@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb");

// Rasporedi ga u interfejs IDbConnection


IDbConnection cn = (IDbConnection)oleDbConn;
// Sada kôd za ovaj interfejs
cn.Open();
IDbCommand cmd = cn.CreateCommand();
cmd.CommandText = "SELECT * FROM Employees";
IDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
Console.WriteLine("{0} {1}", dr["FirstName"], dr["LastName"]);
}

33
Poglavlje 1

Ovde smo formirali objekat veze kao i obično (uostalom, veza će uvek biti specifična za
dobavljača), a zatim smo ga rasporedili u interfejs IDbConnection. Zatim smo u mogućnosti
da pozovemo metode veze kao što je definisano u interfejsu. Na primer, metod
CreateCommand vraća instancu interfejsa IDbCommand umesto instancu klase
OleDbCommand. S obzirom na to da svi dobavljači moraju da podrže pomenute interfejse, ova
tehnika nam omogućava da pišemo kodove koji su u potpunosti nezavisni od dobavljača (osim
polazne veze). Međutim, interfejsi ne obezbeđuju implementacije sopstvenih članova, pa će zato
biti upotrebljene implementacije koje su specifične za dobavljače. Na primer, kada pozovemo
metod IDbCommand.ExecuteReader, metod ExecuteReader klase OleDbCommand će i
dalje biti izvršen.

Upotreba ADO 2.x u .NET okruženju


Iako smo nekoliko puta naglasili da je poželjno koristiti ADO.NET umesto ADO kada god je to
moguće, i dalje postoji nekoliko situacija kad smo prinuđeni da koristimo tradicionalni ADO.
Ovo smo već pomenuli, ali nije na odmet izvršiti kratak pregled na jednom mestu:
❐ U slučaju da morate upotrebiti povezani skup zapisa, a pritom želite da ažurirate izvor
podataka. ADO poseduje fleksibilnije tipove kursora i blokada. Ako vaša aplikacija
ima dovoljno mali broj korisnika za simultano povezivanje, a vama je potrebno da
vidite promene koje ste izvršili u izvoru podataka, onda ADO sigurno predstavlja bolji
izbor.
❐ Ukoliko želite da upotrebite ADO objekte Record i Stream – na primer, ako koristite
OLE DB dobavljač za Exchange 2000 (ExOLEDB) ili Internet izdavaštvo (MSDAIPP).
Sve dok se Ole Db dobavljač ne ažurira tako da može da podrži interfejse OLE DB
2.5 ili .NET dobavljače za pomenute izvore podataka , moraćete da se oslonite na
ADO.

Da bismo koristili ADO 2.x iz .NET okruženja, potrebno je da napravimo Runtime Callable
Wrappers (RCW) za COM komponente. Ovo možemo obaviti u Visual Studio .NET-u, i to
izborom ADODB biblioteke sa kartice COM u okviru za dijalog Add Reference:

34
Pristup podacima i .NET okruženje

Kao alternativa javlja se i mogućnost upotrebe alatke komandne linije TlbImp.exe za uvoz
biblioteke tipa u .NET okruženje:

Primetićete da se ovim ne instalira skup u globalnu keš memoriju skupa (engl. Global Assembly
Cache), te je stoga potrebno da proverimo da li je DLL u istom direktorijumu kao i kôd koji ga
poziva.
Upotreba RCW za ADODB biblioteku slična je njegovoj upotrebi iz VB 6. Međutim, stavka na
koju je potrebno obratiti pažnju ako pišete C# kôd jeste da C# ne podržava opcione parametre
za pozive metoda, pa je zato potrebno uključiti sve parametre. Ovim se pozivi metoda prilično
komplikuju, te nije na odmet imati pri ruci referencu za ADO model objekta.

35
Poglavlje 1

Korišćenje ADO.NET-a
Kao što smo prethodno napomenuli, jedna od najvažnijih funkcija .NET razvojnog okruženja
jeste podrška za više jezika. Iako većina kodova u ovoj knjizi koristi jezik C# , ADO.NET je
podjednako jednostavan za korišćenje iz bilo kog .NET jezika. Da bismo vam olakšali korišćenje
ADO.NET-a, objasnićemo vam kako se piše ADO.NET kôd u svim Microsoft .NET jezicima.
Za ove primere, otvorićemo vezu sa pubs bazom podataka u SQl Serveru, izdvojiti podatke iz
tabele authors u DataReader, a zatim ćemo ponoviti izvršavanje svih iskaza i prikazati polja
au_fname i au_lname za svaki red.

Primer C#
S obzirom na to da je većina primera u ovoj knjizi u jeziku C#, počećemo objašnjenjem ovog
jezika. Do sada smo videli većinu ovih kodova – kôd za otvaranje veze sa SQL serverom
identičan je kodu koji smo videli u odeljku o SqlClinet dobavljaču, dok je kôd za ponavljanje
izvršavanja iskaza u dobavljaču skoro isti kao i primer koji smo upotrebili za upoređivanje
povezanog pristupa u programima ADO i ADO.NET:

using System;
using System.Data.SqlClient;

class CSharpAdoExample
{
static void Main(string[] args)
{
// Napravi objekat SqlConnection i otvori vezu
SqlConnection cn = new SqlConnection(
"Data Source=JULIANS2;Initial Catalog=pubs;User ID=sa;Password=");
cn.Open();

// Napravi i izvrši SqlCommand


SqlCommand cmd = new SqlCommand(
"SELECT au_fname, au_lname FROM authors", cn);
SqlDataReader dr = cmd.ExecuteReader();

// Ponovi izvršavanje iskaza kroz DataReader i prikaži polja au_fname


i au_lname
while (dr.Read())
{
Console.WriteLine(dr["au_fname"] + " " + dr["au_lname"]);
}
}
}

Ako ovu C# izvornu datoteku nazovemo CSharpAdoExample.cs, možemo je kompajlirati iz


komandne linije jednostavnim upisivanjem csc CSharpAdoExample.cs. Nije potrebno
dodati referencu.

Primer Visual Basic .NET-a


VB.NET kôd razlikuje se od C# kôda samo u sintaksi. Iako postoje fundamentalne razlike
između pomenuta dva jezika (poput podrške u C#-u za pokazivače i nesigurne kodove), te
razlike, generalno gledajući, ne utiču direktno na programiranje u ADO.NET-u. Razlog tome
jeste što su ADO.NET klase koje podržavaju CLS i samim tim ne izlažu karakteristike koje ne
podržavaju svi .NET kompajleri.

36
Pristup podacima i .NET okruženje

Imports System
Imports System.Data.SqlClient

Module VBAdoExample

Sub Main()

' Declare our variables


Dim cn As SqlConnection
Dim cmd As SqlCommand
Dim dr As SqlDataReader

' Napravi objekat SqlConnection i otvori vezu


cn = New SqlConnection("Data Source=JULIANS2;" & _
"Initial Catalog=pubs;User ID=sa;Password=")
cn.Open()
' Napravi i izvrši SqlCommand
cmd = New SqlCommand("SELECT au_fname, au_lname FROM authors", cn)
dr = cmd.ExecuteReader()

' Ponovi izvršavanje iskaza kroz DataReader i prikaži au_fname i


au_lname
While dr.Read()
Console.WriteLine(dr("au_fname") & " " & dr("au_lname"))
End While
End Sub

End Module

Zapamtite da ukoliko kompajlirate VB.NET datoteke koje sadrže ADO.NET kôd sa komandne
linije, potrebno je da dodate reference za skupove System.dll i System.Data.dll. Na primer:

vbc VbAdoExample.vb /r:System.dll /r:System.Data.dll

Primer JScript.Neta
JScript.NET predstavlja najjednostavniji jezik. Sintaksa je skoro identična sintaksi u verziji C#,
osim što je JScript.NET, prema podrazumevanim vrednostima, labavo definisanih tipova (stoga
se svi ADO.NET objekti deklarišu kao vars). Takođe, u JScript.NET-u nije potrebno uključiti
kodove aplikativnog nivoa u okviru klase ili bilo koji tip funkcije Main (ovo se generiše putem
JScript kompajlera):

import System;
import System.Data.SqlClient;

// Napravi objekat SqlConnection i otvori vezu


var cn = new SqlConnection(
"Data Source=JULIANS2;Initial Catalog=pubs;User ID=sa;Password=");
cn.Open();

// Napravi i izvrši SqlCommand


var cmd = new SqlCommand("SELECT au_fname, au_lname FROM authors", cn);
var dr = cmd.ExecuteReader();

37
Poglavlje 1

// Ponovi izvršavanje iskaza kroz DataReader i prikaži au_fname i au_lname


while (dr.Read())
{
Console.WriteLine(dr["au_fname"] + " " + dr["au_lname"]);
}

Kao i kod C#-a, nema potrebe za referenciranjem skupova System.dll i


System.Data.dll, pa smo stoga u mogućnosti da kompajliramo izvorni kôd upotrebom jsc
JsAdoExample.js.

Upravljani C++ primer


Programski jezici C# i C++ su sintaksički srodni, ali razlika između C# i upravljanog C++ je
mnogo veća od razlike između jezika C# i VB.NET-a:

// Obuhvaćeno standardno zaglavlje


#include "stdafx.h"

// Referenciraj eksterne skupove


#using <mscorlib.dll>
#using <System.dll>
#using <System.Data.dll>

// Uvezi imenovane prostore


using namespace System;
using namespace System::Data::SqlClient;

// Ovo je ulazna tačka za ovu aplikaciju


#ifdef _UNICODE
int wmain(void)
#else
int main(void)
#endif
{
// Napravi objekat SqlConnection i otvori vezu
String* connectString =
"Data Source=JULIANS2;Initial Catalog=pubs;User ID=sa;Password=";
SqlConnection* cn = new SqlConnection(connectString);
cn->Open();

// Napravi i izvrši SqlCommand


String* cmdString = "SELECT au_fname, au_lname FROM authors";
SqlCommand* cmd = new SqlCommand(cmdString, cn);
SqlDataReader* dr = cmd->ExecuteReader();

// Ponovi izvršavanje iskaza kroz DataReader i prikaži au_fname i


au_lname
while(dr->Read())
{
String* firstName = dr->get_Item("au_fname")->ToString();
String* lastName = dr->get_Item("au_lname")->ToString();
String* authorName = firstName->Concat(firstName, " ", lastName);
Console::WriteLine(authorName);
}
return 0;
}

38
Pristup podacima i .NET okruženje

Primetićete da je potrebno referencirati svaki od spoljašnjih skupova (poput System.Data.dll)


upotrebom direktive #using, kao i dodati direktivu using (ovog puta bez '#') iz uvoza
imenovanog prostora (kao i u C#). Ako do sada niste videli upravljani C++, najočiglednija
razlika u odnosu na C# je u tome što se tipovi reference implementiraju upotrebom sintakse
pokazivača (na primer, String* u jeziku C++ je isto što i string u jeziku C”). ADO.NET
klase su tipovi reference, pa je zato potrebno upotrebiti indirektni operator pristupa članu (->)
umesto direktnog operatora izbora člana (.).
Najvažnija razlika koja postoji u ADO.NET kodu jeste nemogućnost upotrebe naziva kolone za
indeksiranje u DataReader, a zatim i izdvajanje podataka za tu kolonu. Umesto toga, potrebno
je pozvati metod get_Item. Konačno, ne postoji mogućnost povezivanja nizova upotrebom +
operatora, već je potrebno upotrebiti metod Cocat. Ovaj metod uzima do četiri znakovna niza i
povezuje ih u jedan niz. Primetićete da pomenuti metod nije statičan, pa zato mora biti pozvan
preko instance String* iako ne utiče na navedenu instancu.

Primer J#
Sintaksa u jeziku Java veoma je slična onoj u jeziku C#, pa je sledeći primer sličan primeru iz
jezika C#:

import System.*;
import System.Data.*;
import System.Data.SqlClient.*;

// Kratak opis za Class1.


public class Class1
{
public static void main(String[] args)
{
SqlConnection cn = new SqlConnection("Data Source=JULIANS2;" +
"Initial Catalog=pubs;User ID=sa;Password=");
cn.Open();
SqlCommand cmd = new SqlCommand(
"SELECT au_fname, au_lname FROM authors", cn);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
System.out.println(dr.get_Item("au_fname") + " " +
dr.get_Item("au_lname"));
}
}
}

Interesantna stvar vezana za jezik J# jeste mogućnost upotrebe kako tradicionalnih Java paketa,
tako i .NET imenovanih prostora (oba se uvoze upotrebom ključne reči import). Na primer, u
prethodno navedenom kodu, upotrebili smo tradicionalni Java metod System.out.println
za štampanje na konzoli, ali smo isto tako mogli da upotrebimo i .NET metodu
System.Console.WriteLine. Naravno, podrazumeva se da je ADO.NET kôd uvek .NET
specifičan.

39
Poglavlje 1

ADO.NET događaji
Pre nego što završimo ovo poglavlje, predstavićemo vam još jednu karakteristiku ADO.NET-a –
ADO.NET događaj. ADO.NET događaji su veoma slični ADO događajima. Događaji se
generišu kada se nešto značajno dogodi, poput otvaranja ili zatvaranja veze. Kao i u programu
ADO, događaji se izlažu kao članovi ADO.NET objekata i naš kôd može informisati objekat da
želimo biti obavešteni kada se određeni događaj desi. Upravo se tada izvršava specijalno
određeni metod poznat kao modul za upravljanje događajima (engl. event handler).
Iako su događaji u ADO.NET-u pojmovno slični ADO događajima, nažalost, način
implementiranja modula za upravljanje događajima se prilično razlikuje. Da stvar bude još
komplikovanija, način implementiranja se razlikuje u zavisnosti od jezika koji koristite.
Međutim, generalna procedura je ista. Kao prvo, potrebna nam je referenca za objekat koji će
generisati događaje. Zatim, moramo da informišemo objekat o tome da želimo biti obavešteni
kada se određeni događaj desi. Konačno, potrebno je da napišemo modul za rukovanje
događajima koji će biti pozvan svaki put kada se podigne događaj za taj objekat.
Sada ćemo razmotriti na koji način sve ovo funkcioniše u C# i ADO.NET-u. Kao primer
upotrebićemo događaj StateChange klase OleDbConnection koji se generiše svaki put
kada se veza otvori ili zatvori. Naime, događaji u .NET-u koriste specijalnu kategoriju tipa
podataka poznatu kao delegat (engl. delegate). Delegat predstavlja referencu za metod – u ovom
slučaju, za naš metod rukovanja događajem. Da bismo dobijali obaveštenja o događaju,
potrebno je da dodamo pomenuti delegat našem događaju StateChange.
U jeziku C# pomenuti proces obavlja se jednostavnom upotrebom operatora +=. Referencu za
delegat dobijamo tako što ćemo napraviti novi objekat odgovarajućeg tipa modula za rukovanje
događajima (u ovom slučaju, objekat StateChangeEventHandler) i proslediti našu funkciju
u konstruktor:

// kôd C#
cn.StateChange += new StateChangeEventHandler(cn_StateChange);

U programu VB.NET dobijamo referencu za delegata korišćenjem funkcije AddressOf i


dodavanjem pomenutih funkcija događaju StateChange klase OleDbConnection pomoću
funkcije AddHandler:

' kôd VB.NET


AddHandler cn.StateChange, AddressOf cn_StateChange

Sledeće što je potrebno uraditi jeste napisati metod za rukovanje događajima. U ovom slučaju,
napisaćemo novo stanje u konzoli. Moduli za rukovanje događajima poseduju predodređene
potpise i naš metod se mora povinovati ovom pravilu. Uvek postoje dva parametra – objekat
koji je podigao događaj i objekat koji sadrži dodatne argumente. Tip ovog objekta varira u
zavisnosti od tipa modula za rukovanje događajima. Za događaj StateChange, objekat je tipa
StateChangeEventArgs.
Sledi kôd C# za modul za rukovanje događajima:

public static void cn_StateChange(object sender,


System.Data.StateChangeEventArgs e)
{
Console.WriteLine("Connection state changed: {0}",
((OleDbConnection)sender).State);
}

40
Pristup podacima i .NET okruženje

Primetićete da je pošiljalac prosleđen u metod kao tip object, pa je stoga potrebno da izvršimo
njegovu konverziju u stvarni tip pošiljaoca (u ovom slučaju, OleDbConnection) pre samog
pristupanja njegovim metodima i svojstvima.
Jer, ako ne uključimo opciju OptionStrict, VB.NET neće zahtevati pomenutu konverziju tipova,
pa će kôd biti jednostavniji:

Private Sub cn_StateChange(sender As Object, _


e As System.Data.StateChangeEventArgs)
Console.WriteLine("Connection state changed: {0}", sender.State)
End Sub

U suprotnom, biće potrebno da deklarišemo objekat OleDbConnection na nivou klase ili


modula i direktno pristupimo njegovom svojstvu State:

Console.WriteLine("Connection state changed: {0}", cn.State)

Da bismo videli kako sve ovo funkcioniše, sledi kompletan pregled kôda C#. VB.NET kôd
možete preuzeti sa Wroxove Web lokacije.

using System;
using System.Data;
using System.Data.OleDb;

class AdoEventsExample
{
public static void Main()
{
// Formiraj objekat OleDbConnection
OleDbConnection cn = new OleDbConnection(
@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb");

// Priključi naš modul za rukovanje događajima u događaj


cn.StateChange += new StateChangeEventHandler(cn_StateChange);

// Otvori i zatvori vezu kako bi se izvršilo testiranje modula za


rukovanje događajima
cn.Open();
cn.Close();
}

// Definicija za modul za rukovanje događajima


public static void cn_StateChange(object sender,
System.Data.StateChangeEventArgs e)
{
Console.WriteLine("Connection state changed: {0}",
((OleDbConnection)sender).State);
}
}

Rezultat aktiviranja ovog programa biće sledeći:

41
Poglavlje 1

Rezime
Iako ovo poglavlje nije previše obimno, uspeli smo da obradimo veliki deo materije. Međutim,
tek smo zagrebali po površini onoga što ADO.NET može da pruži. Nadamo se da smo vam
pokazali dovoljno da vas ohrabrimo da nastavite sa proučavanjem ovog programa. Sledi brza
rekapitulacija tema koje smo obradili:
❐ Kratak pregled glavnih karakteristika .NET razvojnog okruženja.
❐ Glavni pravci razvoja tehnologija pristupa podacima u proteklih nekoliko godina i
način na koji se ADO.NET uklapa u pomenuti ubrzani razvoj.
❐ Tri .NET dobavljača podataka, njihove osnovne komponente i objekat DataSet.
❐ XML podrška ugrađena u ADO.NET.
❐ Na koji se način ADO.NET razlikuje od ADO 2.6.
❐ ADO.NET događaji.

42

También podría gustarte