Sunteți pe pagina 1din 18

Capitolul 9

ADO.NET

Capitolul 9

ADO .NET
Multe aplicaii distribuite salveaz, prezint i proceseaz date i informaii utiliznd baze de date. n acest sens, .NET Framework are propria tehnologie de acces la date, numit ADO.NET, ce const ntr-un set de clase prin intermediul crora aplicaiile .NET se pot conecta la bazele de date sau surse de date (baze de date aflate pe servere sau fiiere locale), pot executa comenzi (n regim normal sau tranzacional) sau gestiona i procesa date n mod deconectat (local n aplicaie). n cadrul acestui capitol vor fi introduse urmtoarele noiuni: tipuri de obiecte ADO.NET, furnizori, lucrul n mod conectat i deconectat cu o baz de date i tranzacii la nivelul aplicaiei. 9.1 Introducere n arhitectura ADO.NET. Arhitectura ADO.NET este structurat pe mai multe nivele pentru a integra diferitele tipuri de baze i surse de date. n acest scop se folosete modelul furnizorului de date (engl. data provider). Un data provider reprezint un set clase ADO.NET ce permit accesul la un anumit tip de baz de date, conectarea la o baz de date, execuia de comenzi SQL i preluarea rezultatelor comenzilor. Cu alte cuvinte, un provider ofer o interfa ntre sursa de date i aplicaia client. Tipurile de obiectele ce descriu un provider sunt prezentate n tabelul 9-1. Dei nu aparine de un anumit provider un obiect DataSet reprezint o colecie de tabele i relaii pstrate n memorie sub forma unui cache i asupra crora se pot executa operaii (inserare, tergere i actualizare) fr a fi necesar o conexiune permanent cu sursa de date, modificrile fiind efectuate printr-un obiect DataAdapter. Trebuie fcut observaia c ADO.NET nu ofer furnizori de date generici. Fiecare furnizor de date este caracterizat de implementarea proprie a setului de obiecte (Connection, Command, DataAdapter i DataReader), optimizate pentru tipul de RBDMS1 pe care l reprezint. n cadrul .NET

Relational DataBase Management System

165

Sisteme Distribuite

Framework sunt oferii furnizori pentru urmtoarele tipuri de baze de date: SQL Server, Oracle, OLE DB i ODBC.
Tabel 9-1 - Obiecte pe care furnizorul unei baze de date trebuie s le pun la dispoziie
Obiect Connection Command DataReader Descriere Acest obiect este folosit pentru a stabili o conexiune la baza sau sursa de date. Clasa obiectului trebuie s implementeze IDbConnection. Se utilizeaz pentru a executa comenzi SQL i proceduri stocate. Baza acestui obiect o reprezint interfaa IDbCommand. Acest obiect ofer acces rapid la datele provenite dintr-o interogare. Clasa acestuia implementeaz IDataReader. Prin intermediul acestui obiect se pot executa dou operaii: popularea unui DataSet de la o surs de date i aplicarea modificrilor fcute n obiectul DataSet. Clasa implementeaz interfaa IDataAdapter.

DataAdapter

Modelul simplificat al arhitecturii ADO.NET poate fi urmrit n figura 9.1.


Nivel Aplicatie DataSet (acelai pentru toti providerii)

Nivel furnizori ADO.NET


Provider .NET SQL Server Provider .NET OLE DB
Driver Ole DB

DataAdapter DataReader Provider .NET Oracle

Command Connection
Nivel Surse de date

SQL Server DB

OLE DB Data source

Oracle DB

Figura 9.1 Arhitectura ADO.NET

n momentul n care vrem s utilizm o anumit surs de date pentru o aplicaie este important s folosim furnizorul potrivit. n primul rnd trebuie gsit un provider nativ .NET care s suporte sistemul de gestiune al bazei de date. Oracle i SQL Server sunt deja suportate de .NET. 166

Capitolul 9

ADO.NET

Dac nu gsim un astfel de provider se poate utiliza unul din furnizorii OLE DB sau ODBC mpreuna cu driverele aferente tipului de baze date folosit. Primul reprezint o tehnologie ce exist de mai muli ani n ADO clasic i poate fi utilizat pentru baze de date c Oracle, SQL Server, MySQL i MS Access. n cazul n care nu se poate gsi un provider dedicat .NET se poate utiliza ODBC (suportat de .NET) mpreuna cu driverul corespunztor [Mac05]. La prima vedere ADO.NET pare c nu ofer un set implicit de obiecte ce poate fi utilizat pentru mai multe tipuri de baze de date. Dac cercetm clasele de baz i interfeele pe care acestea le implementeaz clasele obiectelor din furnizori observm c sunt aceleai. Cu alte cuvinte furnizorii sunt standardizai astfel nct s funcioneze la fel indiferent de tipul de surs de date folosit. Diferenele ntre furnizori constau din funcii de de nivel sczut (engl. low level) utilizate n comunicarea cu baza de date [Mac05]. ADO.NET ofer un al nivel de standardizare prin utilizarea clasei DataSet. Obiectul de tip DataSet este complet generic fiind utilizat la fel pentru toate sursele de date. Acest lucru ofer avantajul separrii codului de luare a datelor din baza de date i cel de procesare a datelor [Mac05]. 9.2 Obiectele ADO.NET ADO.NET ofer dou tipuri de obiecte: Orientate pe legtura cu sursa de date (engl. connection based): Aceste obiecte sunt specifice tipului de furnizor i ofer API-uri dependente de o conexiune la o surs de date. Aici se regsesc obiectele: Command, Connection, DataReader i DataAdapter. Orientate pe coninut: Aceste obiecte reprezint adaptoare sau wrapper-e pentru date eterogene. Implementrile claselor sunt aceleai pentru toate tipurile de surse de date i de aceea sunt grupate n namespace-ul System.Data. Categoria include obiecte cum sunt: DataSet, DataTable, DataRow, etc.
Obs: ntre cele dou tipuri de obiecte apare i o diferen n ceea ce privete numele claselor: clasele de obiecte connection based sunt prefixate de tipul de provider. De exemplu n cadrul furnizorului de Oracle numele claselor componente vor ncepe cu Oracle. Obiectele orientate pe coninut sunt independente de tipul de surs de date i nu utilizeaz aceasta convenie de nume.

167

Sisteme Distribuite

Clasele ADO.NET sunt grupate n cteva namespace-uri, fiecare furnizor avnd propriul namespace n timp ce tipurile independente se afl ntr-o singur locaie. Pentru folosirea lor trebuie s includem n proiectul nostru o referin ctre System.Data.dll. Descrierea fiecrui namespace ADO.NET este prezentat n tabelul urmtor (tabel 9-2):
Tabel 9-2 - Namespace-uri folosite de ADO.NET
Namespace Descriere Conine tipurile independente de provider i care modeleaz tabele, relaii, vizualizri, constrngeri, etc. n plus conine interfeele de baz implementate de clasele unui provider: IDbConnection, IDbCommand, IDataAdapter i IDataReader. Conine clasele de baz ale ADO.NET. Clasele implementeaz interfee din System.Data. Clasele furnizorilor motenesc funcionalitatea tipurile coninute n acest namespace. Conine clasele furnizorului OLE DB Conine clasele optimizate pentru lucrul cu bazele de date MS SQL Server. Conine clasele folosite de client pentru a accesa o baz de date Oracle Conine clase pentru lucrul cu baze de date cu interfaa ODBC Conine clase care abstractizeaz tipurile native din SQL Server. Acestea ofer o alternativ la tipurile standard de date .NET ce necesit conversie automat.

System.Data

System.Data.Common System.Data.OleDb System.Data.SqlClient System.Data.OracleClient System.Data.Odbc System.Data.SqlTypes

9.3 Obiectul de conectare Obiectele de conectare sunt utilizate pentru a stabili o conexiune la o baz de date, operaie necesar nainte de a folosi obiectele DataAdapter i Command pentru a interaciona cu baza de date. Clasele acestor obiectelor implementeaz interfaa IDbConnection. Aceast interfa specific metode comune, precum Open() sau Close(), pentru toi furnizorii de surse de date. Pe lng cele dou metode interfaa conine i un prototip de proprietate de tipul String, ConnectionString, prin care sunt specificate, sub forma de pereche cheie-valoare, informaii necesare pentru deschiderea unei conexiuni ctre o baza de date. Un connection string pentru o baz de date SQL Server arat astfel: 168

Capitolul 9

ADO.NET

"Data Source=(local);Initial Catalog=Northwind;Integrated Security=SSPI" (1) //sau "Data Source=Server_BD;Initial Catalog=Nume_BD;User ID=UserID; Password=Parola" (2) Obs: 1. Important de reinut e c parametrii setul de parametri de conectare pentru SQL Server i Oracle trebuie dai ntocmai cum sunt specificai n documentaie. Orice spaiu n plus sau orice schimbare de ordine a acestor parametri va duce la obinerea unui connection string invalid. 2. Pentru a uura lucrul cu o baz de date mediu Visual Studio conine un instrument Server Explorer prin intermediul cruia se pot obine informaii despre toate serviciile de baze de date instalate pe calculator precum i bazele de date folosite curent de acestea. 3. Se poate consulta Anexa I.A pentru construirea unui set de parametri n cazul n care se dorete utilizarea altui tip de baz de date dect SQL Server.

Primul exemplu descrie un set de informaii de conectare pentru un server aflat pe maina local i folosete securitatea integrat n care datele de conectare corespund cu cele ale utilizatorului deja logat.
Tabel 9-3 - Parametrii ce formeaz setul de informaii de conectare;pentru list complet a acestor parametri se va consulta Anexa I.B
Parametru Data Source Initial Catalog Integrated Security User ID Password Descriere Identific serverul printr-o adres IP sau un nume din domeniu. Numele bazei de date. Specific tipul de securitate integrat. Dac este setat cu SSPI (Security Support Provider Interface) se vor folosi informaiile de autentificare ale utilizatorului logat curent. Numele unui utilizator configurat pe SQL Server. Parola pentru utilizator. Specific dac o conexiune poate fi preluat dintr-un pool de conexiuni. Acest mod este eficient din punct de vedere al performanei ntruct crearea unei noi conexiuni poate fi consumatoare de timp. Parametrul poate lua valorile true sau false. Implicit aceast opiune este true. Specific numrul minim de conexiuni pentru pool-ul de conexiuni. Specific numrul maxim de conexiuni pentru pool-ul de conexiuni.

Pooling

Min Pool Size Max Pool Size

169

Sisteme Distribuite

Al doilea exemplu conecteaz un utilizator la o baz de date folosind un nume de utilizator i o parol. n cazul n care folosim SQL Server, pentru setul de informaii de conexiune avem urmtorii parametri importani (tabel 9-3). Modul de folosire a obiectului de conexiune este urmtorul (listing 9-1).
Listing 9-1 - Folosirea obiectului de Conectare ntr-un client de SQL Server String conString = (string)ConfigurationManager.ConnectionStrings["northwind"].ConnectionString; SqlConnection con = new SqlConnection(conString); try { //deschid conexiunea con.Open(); Console.WriteLine("Conexiune deschisa. Parametri: "+conString); } catch(Exception ex) { Console.WriteLine(ex.Message); } finally { //nchid conexiunea con.Close(); }

n exemplul anterior, setul de parametri de conectare a fost luat din fiierul de configurare al aplicaiei. Aceast practic este indicat ntruct acest set de conectare se poate schimba n funcie de maina pe care a fost instalat aplicaia server fcnd inutil meninerea ei ntr-o constant n interiorul codului. 9.4 Obiectele Command i DataReader Obiectul Command, aa cum menionam mai sus, permite execuia unei comenzi pe server i preluarea rezultatelor. Pentru a nelege modul de lucru trebuie prezentai membrii interfeei IDbCommand, interfa pe care clasa obiectului Command o implementeaz. Tabelul 9-4 prezint cei mai importani membri. Un DataReader este asemntor unui stream de obiecte. Dac avem nevoie s accesm nregistrri ntr-un mod secvenial i ntr-o singur direcie, putem folosi un obiect DataReader deoarece este mai eficient dect 170

Capitolul 9

ADO.NET

alte mecanisme. ntruct n timpul citirii cu DataReader conexiunea aferent trebuie s fie deschis este bine ca operaiunea de citire din DataReader s nu fie de lung durat. Un obiect DataReader valid este obinut prin execuia metodei ExecuteReader() a unui obiect de tip Command. Dac este instaniat folosit new nu va putea fi folosit la nimic.
Tabel 9-4 - Membri interfeei IDbCommand
Membri CommandText Descriere Proprietate de tip String cu mai multe semnificaii. De regul conine o comand sau un nume procedur stocat. Proprietate de tip CommandType prin intermediul creia se precizeaz cum va fi interpretat corpul CommandText. Dac are valoarea CommandType.Text, aceasta din urm va fi interpretat drept o comand, dac are valoarea CommandType.StoredProcedure atunci CommandText va desemna numele unei proceduri stocate iar pentru CommandType.TableDirect, CommandText va fi interpretat ca numele tabelului pentru care comanda va prelua toate nregistrrile. Proprietate de tip folosit.
IDbConnection

CommandType

Connection Transaction

ce seteaz conexiunea

Proprietate de tipul IDbTransaction prin care se specific tranzacia pe care aceast comand se execut. Este metoda prin care sunt executate comenzi care nu ntorc nregistrri, adic insert, delete i update. Ele returneaz numrul de nregistrri afectate. Este metoda prin care se ntoarce elementul situat pe prima linie i prima coloan a tabelului returnat. De obicei aceast metod este folosit pentru a executa comenzi ce conin funcii SQL AVG(), SUM(),COUNT(). Execut comenzi SELECT i returneaz un obiect DataReader

ExecuteNonQuery()

ExecuteScalar()

ExecuteReader()

Pentru a citi din nou rndurile trebuie creat al obiect DataReader. Dup ce a fost folosit obiectul, trebuie apelat metoda Close() pentru a elibera resursele. Exemplul urmtor prezint cele menionate anterior (listing 9-2). 171

Sisteme Distribuite

Listing 9-2 Utilizarea obiectelor DataReader i Command // 1. instaniaz un nou obiect de tip conexiune SqlConnection conn = new SqlConnection(@"Data Source=localhost\SQLEXPRESS;Initial Catalog=northwind;Integrated Security=SSPI"); SqlDataReader rdr = null; try { conn.Open();// 2. Deschide conexiunea // 3. Folosete obiectul de conexiune la creare unui obiect Command SqlCommand cmd = new SqlCommand("select * from employees", conn); // 4.Execut comanda i preia rezultatele interogrii rdr = cmd.ExecuteReader(); // afieaz toate cmpurile while (rdr.Read()) { for(int i = 0; i < rdr.FieldCount ; i++) { Console.Write(rdr[i]+" ");} Console.WriteLine(""); } } catch(Exception ex) { Console.WriteLine(ex.Message); } finally { // nchide reader-ul if (rdr != null) { rdr.Close(); } // 5. nchide conexiunea if (conn != null) { conn.Close(); } } Obs: Codul anterior poate fi executat din cadrul proiectului connObj dat ca exemplu pentru acest capitol.

Metoda IDbCommand.ExecuteReader() conine i o variant n care aceasta primete o valoare a tipului enum CommandBehavior prin care se descrie rezultatul interogrii dar i comportamentul obiectului DataReader. De exemplu dac vrem s ntoarcem o singur nregistrare se poate folosi CommandBehavior.SingleRow pentru a optimiza execuia comenzii. Sau se poate specifica nchiderea automat a conexiunii dup apelul metodei DataReader.Close(), prin valoarea CommandBehavior.CloseConnection. 172

Capitolul 9

ADO.NET

Pn la .NET 2.0, numai o singur comand putea fi executat la un moment dat pentru o conexiune stabilit. ncepnd cu ADO.NET 2.0 i SQL Server 2005 .NET este introdus tehnica MARS. Acesta este o nou facilitate n este permis execuia multipl de interogri sau proceduri stocate folosind o singur conexiune. Opiunea MARS este activat atunci cnd se folosete clientul de SQL Server pentru conectare la sursa de date. Se poate specifica i explicit prin adugarea parametrului MultipleActiveResultSets, setat pe true, n setul de conectare:
string northwindConnectionString = "Server=localhost;Database=Northwind;" + "Trusted_Connection=True; MultipleActiveResultSets=True";

nainte de a folosi MARS trebuie menionate urmtoarele: MARS ofer o alternativ la aplicaiile care utilizeaz conexiuni multiple. MARS nu implic i un spor de performan deoarece realizarea de conexiuni multiple la server implic execuia n paralel a mai multor interogri. Modul de execuie al interogrilor pentru MARS rmne secvenial; n cazul aplicaiilor cu mai multe fire de execuie trebuie inut cont de faptul c MARS nu este protejat de efectele nedorite ale paralelismului (engl. thread safe); Cnd se deschide o conexiune cu MARS activat, o sesiune logic este creat. Un client de SQL Server va face un cache al sesiunilor pentru a mri performana. Acest cache va conine pn la 10 sesiuni MARS. Cnd se ajunge la limit o nou sesiune este creat automat. 9.5 Obiecte orientate pe coninut n exemplele anterioare, datele, dup ce au fost preluate n urma execuiei unei comenzi nu mai au nici o legtur cu sursa de date. Cu alte cuvinte nu exist nici un mecanism prin care datele preluate sunt asociate cu cele din sursa de date de unde au fost extrase. Singurul moment n care acest lucru se ntmpl este acela n care conexiunea este activ iar datele sunt citite. Soluia folosit n ADO.NET pentru lucrul n mod deconectat cu sursa de date este clasa DataSet. Cei reprezentativi membri ai acestei clase sunt prezentai n tabelul urmtor (tabel 9-5). 173

Sisteme Distribuite

Tabel 9-5 - Membri clasei DataSet


Membri HasChanges(), HasErrors(), GetChanges(), AcceptChanges() RejectChanges() Descriere Cu ajutorul acestui set de metode modificrile de date din pot fi actualizate cu uurin n baza de date la un moment ulterior: se pot vedea modificrile din DataSet, se pot obine modificrile sub forma unui DataSet modificat, se pot verifica erorile survenite n modificri, i, de asemenea, putem accepta sau respinge modificrile. Dac vrem s transmitem modificrile ctre baza de date trebuie doar s cerem DataSet-ului s fac sincronizarea.
DataSet

GetXml() i GetXmlSchema() ReadXml() i ReadXmlSchema() WriteXml() i WriteXmlSchema()

Returneaz un obiect String, formatat XML, ce conine datele sau schema obiectului DataSet. Schema conine informaii cum ar fi: numrul tabelelor, relaiile, coloanele i tipurile de date. Creeaz tabele n DataSet pe baza informaiilor existente n documentul XML sau schem. Sursa XML poate fi un fiier sau un stream. Salveaz datele sau schema (n format XML) ntr-un stream sau un fiier.

Ideea folosirii unui DataSet este urmtoarea: dup conectarea la baza de date obiectul DataSet este ncrcat cu tabele i relaiile dorite iar apoi se trece la procesarea datelor fr a fi necesar o conexiune permanent. Schimbrile efectuate nu afecteaz baza de date ntruct se lucreaz local i nu direct pe server. Pentru a aplica modificrile trebuie realizat o nou conexiune i apoi aplicate schimbrile. Exist un mecanism automat de aplicare a acestor schimbri astfel nct utilizatorul este scutit, n cele mai multe cazuri, de a scrie comenzile SQL aferente. DataSet este proiectat n beneficiul aplicaiilor WEB Enterprise care sunt deconectate prin natura lor. n acest caz nu tim dac datele din baza de date s-au modificat pn cnd nu reactualizm nregistrrile sau nu facem alte task-uri care necesit sincronizarea cu baza de date. Structura clasei DataSet este alctuit din dou componente de baz: o colecie de tabele (DataTable) i una de relaii (DataRelation). Figura 9.2 prezint structura intern n detaliu.

174

Capitolul 9

ADO.NET

DataSet DataTableCollectio DataTable DataRowCollection DataRow DataColunmCollection DataColumn DataView DataRelationCollectio DataRelation

Figura 9.2 - Structura obiectului DataSet

Codul exemplific modul de construcie i folosire a unui DataSet (listing 9-3):


Listing 9-3 - Crearea i utilizarea unui obiect DataSet DataSet myDataSet = new DataSet("Dynamic DataSet"); //adaug o nou tabel numita Order DataTable dtOrder = myDataSet.Tables.Add("Order"); //adaug coloanele acestei tabele dtOrder.Columns.Add("OrderId", typeof(int)); dtOrder.Columns.Add("CustomerFirstName", typeof(string)); dtOrder.Columns.Add("CustomerLastName", typeof(string)); dtOrder.Columns.Add("Date", typeof(DateTime)); //setez OrderId c i cheie primar dtOrder.PrimaryKey = new DataColumn[]{dtOrder.Columns["OrderId"]}; //adaug o tabel numit OrderDetails DataTable dtOrderDetails = myDataSet.Tables.Add("OrderDetails"); //adaug coloanele pentru OrderDetails dtOrderDetails.Columns.Add("fk_OrderID", typeof(int)); dtOrderDetails.Columns.Add("ProductCode", typeof(string)); dtOrderDetails.Columns.Add("Quantity", typeof(int)); dtOrderDetails.Columns.Add("Price", typeof(decimal)); //adaug o relaie tip foreign key ntre tabele DataRelation relation = myDataSet.Relations.Add("Order_OrderDetails", dtOrder.Columns["OrderId"], dtOrderDetails.Columns["fk_OrderID"]); relation.Nested = true;

Secvena precedent de cod conine cteva aspecte importante de menionat. Dup instanierea obiectului DataSet adugm dou tabele cu 175

Sisteme Distribuite

ajutorul metodei Add() a coleciei returnate de proprietatea Tables. Facem un efort similar pentru a aduga coloanele n fiecare tabel al DataSet-ului. Pentru a construi cheia primar a tabelului Order construim o colecie de obiecte DataColumn n care introducem coloana OrderID dup care aceast coloan o folosim pentru a seta proprietatea PrimaryKey a tabelului. Pentru a reprezenta relaia dintre cele dou tabele construim un obiect DataRelation numit Order_OrderDetail cu cele dou coloane din cele dou tabele. Urmtoarea secven de cod arat cum se pot insera nregistrri n cele dou tabele (listing 9-4):
Listing 9-4 - Inserarea nregistrrilor intr-un tabel ce aparine unui DataSet //creez o instan de tip DataRow pentru tabelul Orders DataRow dr = dtOrder.NewRow(); dr["OrderId"] = 10; dr["CustomerFirstName"] = "John"; dr["CustomerLastName"] = "Doe"; dr["Date"] = DateTime.Now; //inserez nregistrarea dtOrder.Rows.Add(dr); //creez o instan de tip DataRow pentru tabelul Orders dr = dtOrder.NewRow(); dr["OrderId"] = 11; dr["CustomerFirstName"] = "Jane"; dr["CustomerLastName"] = "Doe"; dr["Date"] = DateTime.Now; //inserez nregistrarea dtOrder.Rows.Add(dr); //creez o intan de tip DataRow pentru tabelul OrdersDetails dr = dtOrderDetails.NewRow(); dr["fk_OrderID"] = 10; dr["ProductCode"] = "Item A"; dr["Quantity"] = 2; dr["Price"] = 7.5; //inserez nregistrarea dtOrderDetails.Rows.Add(dr); //creez o instan de tip DataRow pentru tabelul OrdersDetails dr = dtOrderDetails.NewRow(); dr["fk_OrderID"] = 11; dr["ProductCode"] = "Item B"; dr["Quantity"] = 3; dr["Price"] = 14.2; dtOrderDetails.Rows.Add(dr); //inserez nregistrarea

Un alt tip de obiect ce permite lucrul deconectat de la sursa de date este i clasa DataTable. Acesta menine o colecie de obiecte DataColumns, accesibil prin proprietatea Columns i o colecie de DataRows accesibil 176

Capitolul 9

ADO.NET

prin intermediul proprietii Rows. Proprietatea Columns prezint structura tabelului iar proprietatea Rows ofer acces la nregistrri. Urmtoarea secven de cod care tiprete numele fiecrei coloane i datele din fiecare rnd (listing 9-5):
Listing 9-5 - Exemplu de utilizare a obiectelor DataTable, DataRow i DataColumn //obin o referin la tabelul Orders DataTable dt = myDataSet.Tables["Order"]; //iterez peste toate coloanele tabelului foreach (DataColumn dc in dt.Columns) { Console.Write (String.Format("{0}\t", dc.ColumnName); } Console.WriteLine(Inregistrari:); foreach(DataRow row n dt.Rows) { foreach(DataColumn dc n dt.Columns) { Console.Write(String.Format("{0}\t", row[dc])); } Console.WriteLine(); }

n mod obinuit un DataTable conine unul sau mai multe cmpuri care au rol de cheie primar. Activarea acestei funcionaliti este expus de proprietatea PrimaryKey. Deoarece cheia primar poate fi compus din mai multe coloane proprietatea este de tipul unui colecii de DataColumn. A doua component important pentru un DataSet este colecia de relaii ntre tabele alctuit din obiecte DataRelation. Componentele client pot inspecta un anumit tabel sau pot naviga printr-o ierarhie de tabele cu ajutorul acestora. Spre exemplu putem gsi o anumit nregistrare ntr-un tabel printe i s parcurgem toate nregistrrile dependente dintr-un tabel copil. Clasa DataRelation conine numele tabelului printe, numele tabelului copil, coloana din tabelul printe (primary key) i coloana din tabela copil (foreign key). ntr-o baz de date relaiile descriu legturile ntre tabele. DataSet-ul pstreaz colecia de relaii dintre tabelele pe care le conine, n proprietatea Relations. Cu toate acestea, fiecare tabel care particip n cadrul unei relaii trebuie s pstreze aceast informaie. Astfel tabelul expune dou proprieti ChildRelations i ParentRelations prin care putem determina n ce relaii este implicat tabelul. ChildRelations enumer toate relaiile n care tabelul particip ca tabel printe iar ParentRelations enumer pe acelea n care tabelul particip ca tabel copil. Pentru a lucra cu un obiect DataSet este important s nelegem modul n care putem s stabilim constrngeri. Exist dou tipuri de constrngeri: UniqueConstraint i ForeignKeyConstraint. Prima oblig 177

Sisteme Distribuite

unicitatea valorii cmpului pentru o tabel iar a doua foreaz regulile de date de relaiile ntre tabele. Pentru a stabili modul felul n care vor fi tratate actualizare sau tergerea unei nregistrri dintr-un tabel se pot seta valorile proprietilor UpdateRule i DeleteRule din clasa ForeignKeyConstraint. Constrngerile sunt active doar cnd proprietatea DataSet.EnforceConstraint este setat pe true. Urmtoarea linie de cod arat cum putem modifica constrngerea de cheie strina dintre tabelele Order i OrderDetail pentru a permite tergerea n cascad (listing 9-6):
Listing 9-6 - Setarea regulii de tergere n cascad pentru cheia primar a unui tabel DataRelation dr = myDataSet.Relations["Order_OrderDetails"]; ForeignKeyConstraint f_key = dr.ChildKeyConstraint; F_key.DeleteRule=Rule.Cascade;

Alt clas ce permite lucrul deconectat cu sursa de date este clasa


DataView, similar cu un view dintr-o baz de date. Putem crea diferite

view-uri particularizate, fiecare cu filtrele sale de sortare i filtrare. n aceste view-uri putem face parcurgeri, cutri, edita diferite nregistrri. Obiectele DataView sunt utile atunci cnd le folosim ca surs de date pentru diferitele controale din ASP.NET sau Windows Forms. 9.6 Obiectul DataAdapter Clasa DataAdapter face legtur ntre sursa de date i obiectul DataSet. Ea conine o conexiune i un set de comenzi pentru obinerea nregistrrilor din baza de date, plasarea acestora n tabele din DataSet i actualizarea datelor din baza de date cu schimbrile din DataSet. Modul de interaciune al unui DataAdapter cu sursa de date se poate vizualiza n figura urmtoare (figura 9.3).
IDbDataAdapter SelectCommand InsertCommand UpdateCommand Sursa date DeleteCommand

Obiect Connection

DataSet

Figura 9.3 Rolul unui DataAdapter: interfaa ntre sursa de date i DataSet

Pentru figura 9.3 trebuie fcute cteva observaii. 178

Capitolul 9

ADO.NET

Orice obiect data adapter trebuie s extind clasa abstract


DbDataAdapter, clas construit dup contractul interfeei IDbDataAdapter. Aceast interfa definete patru proprieti de tip IDbCommand prin

intermediul crora sunt executate selecia, tergerea, inserarea i actualizarea asupra tabelei din baza de date. Clasa DbDataAdapter extinde la rndul ei clasa abstract DataAdapter ce implementeaz interfaa IDataAdapter. Cele mai importante metode ale IDataAdapter sunt prezentate n tabelul 9-6. Pentru obinerea unui tabel ntr-un DataSet, un DataAdapter folosete comanda de tip SELECT pstrat de proprietatea SelectCommand. Concret datele sunt aduse prin apelul metodei IDataAdapter.Fill(). Pentru a actualiza datele, un obiect DataAdapter folosete comenzile UPDATE, INSERT i DELETE date de cmpurile ilustrate n figura 9.3. Aceste comenzi sunt utilizate n funcia IDataAdapter.Update().
Tabel 9-6 - Cei mai importani membri ai interfeei IDataAdapter
Membri Fill() Descriere Adaug un obiect DataTable la DataSet-ul curent executnd comanda de selecie din SelectCommand. Dac sunt returnate mai multe seturi de date atunci se vor aduga mai multe obiecte DataTable. Adaug un DataTable la DataSet executnd interogarea din SelectCommand. Aceast metod preia doar schema tabelului mpreun cu cheile primare, numele coloanelor, tipurile de date i constrngeri. Tabelul nu va conine nici o nregistrare Examineaz toate schimbrile fcute ntr-un singur DataTable i aplica un set de schimbri sursei de date executnd comenzile potrivite. Aceste comenzi sunt luate din proprietile interfeei IDbDataAdapter: InsertCommand, DeleteCommand i UpdateCommand.

FillSchema()

Update()

Cu toate c un DataAdapter asociaz doar un singur DataTable din DataSet putem avea mai muli DataAdapter pentru a umple DataSet-ul cu mai multe obiecte DataTable. Codul urmtor realizeaz acest lucru (listing 9-7).
Listing 9-7 - Utilizarea obiectelor DataAdapter pentru a aduga tabele intr-un DataSet DataSet myDataSet = new DataSet("myDataSet"); String connectionString = (string)ConfigurationManager.ConnectionStrings["pubs"].ConnectionString; SqlDataAdapter dataAdapter1 = new SqlDataAdapter("select * from authors", connectionString); SqlDataAdapter dataAdapter2 = new SqlDataAdapter("select * from titles", connectionString);

179

Sisteme Distribuite

SqlDataAdapter dataAdapter3 = new SqlDataAdapter("select * from titleauthor", connectionString); dataAdapter1.Fill(myDataSet, "authors"); dataAdapter2.Fill(myDataSet, "titles"); dataAdapter3.Fill(myDataSet, "titleauthor"); myDataSet.Relations.Add("authors2titleauthor", myDataSet.Tables["authors"].Columns["au_id"], myDataSet.Tables["titleauthor"].Columns["au_id"]); myDataSet.Relations.Add("titles2titleauthor", myDataSet.Tables["titles"].Columns["title_id"], myDataSet.Tables["titleauthor"].Columns["title_id"]);

9.7 Tranzacii O tranzacie reprezint o list de operaii ce trebuie tratate n mod unitar: dac o singur operaie eueaz atunci se consider c tranzacia a euat. Dac toate au reuit atunci tranzacia se consider c a reuit. Exist trei tipuri de tranzacii ce pot fi folosite de aplicaiile .NET mpreun cu SQL Server: Folosind proceduri stocate: se poate scrie cod SQL ce poate gestiona tranzacii. Iniiate de clieni ADO.NET: .NET ofer posibilitatea de a crea i administra programatic tranzacii. COM+: tranzacii administrate de runtime-ul COM+. Majoritatea furnizorilor ofer suport pentru tranzacii. Lansarea unei tranzacii se face prin apelul metodei IDbConnection.BeginTransaction(). Metoda returneaz un obiect de tipul IDbTransaction. Aceast interfa conine n definiia ei urmtorii membri importani prezentai n tabelul 9-8.
Tabel 9-7 - Membri interfeei IDbTransaction
Membri Connection Commit() Rolback() Descriere Proprietate de tip IDbConnection ce specific conexiunea asociat cu aceast tranzacie Aceast metod identific faptul c tranzacia a reuit i c modificrile trebuie salvate n sursa de date. Aceast metod indic faptul c tranzacia nu a reuit iar modificrile trebuie anulate.

n exemplul urmtor se ncearc inserarea n tabela Customers a dou nregistrri n mod tranzacional: 180

Capitolul 9

ADO.NET

Obs: Dac exist operaii ncheiate cu succes atunci obiectele asupra crora au intervenit acestea revin la starea iniial. Procesul se numete rollback. Dac toate operaiile sunt ncheiate cu succes atunci tranzacia este validat iar starea obiectelor este salvat. Aceast operaie se numete commit. O tranzacie trebuie s se supun celor patru reguli ACID: atomicitatea, consistenta, izolarea si durabilitatea. Listing 9-8 - LSD10: Transactii\Program.cs : Program.Main() exemplu de tranzacie folosind SQL Server string conString = (string)ConfigurationManager.ConnectionStrings["northwind"].ConnectionString; SqlConnection con = new SqlConnection(conString); SqlCommand cmd1 = new SqlCommand( "insert INTO Customers(CustomerID,ContactName,CompanyName)VALUES ('JHAPP','John','Apple')" , con); SqlCommand cmd2 = new SqlCommand( "INSERT INTO Customers (CustomerID,ContactName,CompanyName) VALUES ('INMAR','Ion','Marul')" ,con); SqlTransaction tran = null; try { con.Open(); tran = con.BeginTransaction();//ncep tranzacia //specific ce comenzi fac parte din lista de operaii //ce compun tranzacia cmd1.Transaction = tran; cmd2.Transaction = tran; cmd1.ExecuteNonQuery(); cmd2.ExecuteNonQuery(); //commit tran.Commit(); Console.WriteLine("Tranzactie reusita!!!"); } catch (Exception ex) { Console.WriteLine("Tranzactie nereusita, cauza:" + ex.Message); tran.Rollback(); } finally { con.Close(); }

Trebuie remarcat c adugarea operaiilor la lista tranzaciei se face explicit prin setarea IDbCommand.Transaction cu obiectul IDbTransaction rezultat din urma apelului metodei BeginTransaction() asupra obiectului Connection. 181

Sisteme Distribuite

Pentru SQL Server se pot crea puncte intermediare la care se poate reveni mai trziu. Pentru salvarea strii ntr-un anumit punct se utilizeaz metoda SqlTransaction.Save(nume_save_point). Revenirea la starea salvat se face prin SqlTransaction.Rollback(nume_save_point). Tem Realizai o aplicaie care s citeasc datele dintr-o baz de date (dou tabele cu o relaie ntre ele), s afieze datele ntr-un DataGrid, s permit editarea i salvarea modificrilor.

182

S-ar putea să vă placă și