34
1 Pristup podacima i .NET 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.

Pristup podacima i .NET okruženje - mikroknjiga.rs Ado.Net Profesional.pdf · 1 Pristup podacima i .NET okruženje U ovom poglavlju napravićemo kratak pregle d programa ADO.NET

Embed Size (px)

Citation preview

1Pristup podacima i .NET

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ženjeNeć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

10

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).

Pristup podacima i .NET okruženje

11

Proces sakupljanja otpadakaJedna 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.

Poglavlje 1

12

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 jezikaJedna 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 klasaSledeć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.

Pristup podacima i .NET okruženje

13

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 podacimaU 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.

ODBCMicrosoft 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.

DAOJedan 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.

Poglavlje 1

14

RDOZbog 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 DBJoš 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 2000 Web 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.

ADOActiveX 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.

Pristup podacima i .NET okruženje

15

Uvod u ADO.NETIako 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 klasaAko 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.

Poglavlje 1

16

Optimizovani model objektaVeoma 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-aSada 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 podatakaSvaki .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

Komanda

DataReader

DataAdapter

Izvorpodataka

Pristup podacima i .NET okruženje

17

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 komandiKlase 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 SqlKolekcija parametara IData

Parametar Sql:Parametar IData

Poglavlje 1

18

DataReaderKomponenta 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.

DataAdapterPoslednja 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:

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.

SqlClient.NET dobavljač podataka

SqlDataAdapter:IDataAdapter,IDbDataAdapter

SelectCommand

Inser tCommand

UpdateCommand

DeleteCommand

SqlCommand:IDbCommand

SqlCommand:IDbCommand

SqlCommand:IDbCommand

SqlCommand:IDbCommand

Pristup podacima i .NET okruženje

19

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#:

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.

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

Poglavlje 1

20

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):

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:

using System.Data.OleDb; // The OleDb provider namespace // Formiraj objekat OleDbConnectionOleDbConnection cn = new OleDbConnection( @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb");cn.Open(); // Open the connection

Pristup podacima i .NET okruženje

21

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):

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 DataSetJoš 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:

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

using System.Data.OleDb; // imenovani prostor dobavljača OleDb // Formiraj objekat OleDbConnectionOleDbConnection cn = new OleDbConnection( "DRIVER={mySQL};SERVER=JULIAN;DATABASE=Northwind;UID=root;PWD=");cn.Open(); //Otvori vezu

Poglavlje 1

22

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

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.

.NET dobavljač podataka

Veza

Komanda

DataReader

DataAdapter

Izvorpodataka

DataSet

XMLdokument

// Otvori vezuOleDbConnection cn = new OleDbConnection( @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb");cn.Open(); // Napravi novi objekat DataAdapter i prosledi komandu SELECTOleDbDataAdapter da = new OleDbDataAdapter( "SELECT EmployeeID, FirstName, LastName FROM Employees", cn); // Napravi novi DataSetDataSet ds = new DataSet(); // Popuni DataSetda.Fill(ds, "Employees"); // Zatvori vezu; sada imamo podatkecn.Close();

Pristup podacima i .NET okruženje

23

Klasa DataTableOvaj 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:

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:

DataSet

DataTableCollection

DataTable

DataColumnCollection

DataRowCollection

DataColumn

DataRow

// Prikaži nazive koloneforeach (DataColumn dc in ds.Tables[0].Columns) Console.Write("{0,15}", dc.ColumnName);

// Dodaj novu liniju posle zaglavlja koloneConsole.Write("\n");

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

Poglavlje 1

24

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 podatakaAko 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'':

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");}

// Promeni vrednost u objektu DataSetds.Tables[0].Rows[8]["FirstName"] = "Anna"; // Izvrši ponovno povezivanje sa izvorom podatakacn.Open(); // Napravi objekat OleDbCommandBuilder i prosledi objekat DataAdapter// u konstruktorOleDbCommandBuilder cmdBuilder = new OleDbCommandBuilder(da); // Prikaži UpdateCommandConsole.WriteLine(cmdBuilder.GetUpdateCommand().CommandText); // Ažuriraj izvor podataka

Pristup podacima i .NET okruženje

25

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 XMLJedna 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:

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>

da.Update(ds.Tables[0]); // Zatvori vezucn.Close();

// Sačuvaj objekat DataSet kao XML datotekuds.WriteXml(@"C:\CSharp\Employees.xml");

Poglavlje 1

26

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):

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:

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

// Uskladišti XML dokument u nizustring xmlDoc = @"<?xml version='1.0'?> <books> <book> <title>Pro ADO.NET</title> <publisher>Wrox Press</publisher> </book> </books>"; // Učitaj ovo u StringReaderStringReader sr = new StringReader(xmlDoc); // Napravi novi objekat DataSet i čitaj u XMLDataSet ds = new DataSet();ds.ReadXml(sr); // Prikaži nazive kolone i podatke reda u obliku tabeleforeach (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]);}

Pristup podacima i .NET okruženje

27

Definisani DataSetsS 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:

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:

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:

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:

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");

xsd Employees.xsd /d

csc /t:library Employees.cs

Poglavlje 1

28

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.

// 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 DataSetNewDataSet ds = new NewDataSet();da.Fill(ds, "Employees");cn.Close(); // Prikaži podatke za svaki redforeach (NewDataSet.EmployeesRow dr in ds.Employees.Rows){ Console.WriteLine("{0,15}{1,15}", dr.FirstName, dr.LastName);}

Pristup podacima i .NET okruženje

29

Pristup isključenim podacimaADO.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):

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:

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.)

Dim cn As ADODB.ConnectionDim 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 typers.CursorLocation = adUseClient ' Specify that we're using ' OLE DB's Client Cursor Enginers.CursorType = adOpenStatic ' Specify the cursor typers.Open "SELECT * FROM authors", objConn ' Open the recordsetSet objRec.ActiveConnection = Nothing ' Disconnect the recordsetobjConn.Close ' Close the connectionSet objConn = Nothing

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();

Poglavlje 1

30

Pristup samo za čitanje, samo za prosleđivanjeSledeć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:

ADO.NET ekvivalenat bio bi sledeći:

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.

Dim cn As ADODB.ConnectionDim rs As ADODB.Recordset

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

Set rs = New ADODB.Recordsetrs.Open "SELECT * FROM Employees", cn, adOpenForwardOnly, adLockReadOnly

While Not rs.EOF MsgBox rs!FirstName & " " & rs!LastName rs.MoveNextWend rs.Closecn.Close

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();

Pristup podacima i .NET okruženje

31

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čaSledeć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:

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.

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

Poglavlje 1

32

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 ConnectionState State { virtual get; }

// Events

// 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 string DataSource { get; } public string Provider { get; } public string ServerVersion { get; }

public event OleDbInfoMessageEventHandler InfoMessage; public event StateChangeEventHandler StateChange;

public virtual System.Runtime.Remoting.ObjRef CreateObjRef(Type requestedType); public System.Data.DataTable GetOleDbSchemaTable(Guid schema, object[]

Pristup podacima i .NET okruženje

33

public virtual void Open();

} // 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:

restrictions);

public static void ReleaseObjectPool();

// Formiraj objekat veze kao i običnoOleDbConnection oleDbConn = new OleDbConnection( @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\NWind.mdb"); // Rasporedi ga u interfejs IDbConnectionIDbConnection cn = (IDbConnection)oleDbConn;// Sada kôd za ovaj interfejscn.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"]);}

Poglavlje 1

34

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:

Pristup podacima i .NET okruženje

35

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.

Poglavlje 1

36

Korišćenje ADO.NET-aKao š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:

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.

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"]); } }}

Pristup podacima i .NET okruženje

37

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:

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):

Imports SystemImports 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

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

import System;import System.Data.SqlClient; // Napravi objekat SqlConnection i otvori vezuvar cn = new SqlConnection( "Data Source=JULIANS2;Initial Catalog=pubs;User ID=sa;Password=");cn.Open(); // Napravi i izvrši SqlCommandvar cmd = new SqlCommand("SELECT au_fname, au_lname FROM authors", cn);var dr = cmd.ExecuteReader();

Poglavlje 1

38

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++ primerProgramski 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:

// Ponovi izvršavanje iskaza kroz DataReader i prikaži au_fname i au_lnamewhile (dr.Read()){ Console.WriteLine(dr["au_fname"] + " " + dr["au_lname"]);}

// Obuhvaćeno standardno zaglavlje#include "stdafx.h"

// Referenciraj eksterne skupove#using <mscorlib.dll>#using <System.dll>#using <System.Data.dll>

// Uvezi imenovane prostoreusing namespace System;using namespace System::Data::SqlClient;

// Ovo je ulazna tačka za ovu aplikaciju#ifdef _UNICODEint wmain(void)#elseint 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;}

Pristup podacima i .NET okruženje

39

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#:

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.

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")); } }}

Poglavlje 1

40

ADO.NET događajiPre 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:

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:

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:

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

' kôd VB.NETAddHandler cn.StateChange, AddressOf cn_StateChange

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

Pristup podacima i .NET okruženje

41

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:

U suprotnom, biće potrebno da deklarišemo objekat OleDbConnection na nivou klase ili modula i direktno pristupimo njegovom svojstvu 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.

Rezultat aktiviranja ovog programa biće sledeći:

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

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

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); }}

Poglavlje 1

42

RezimeIako 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.