Introduzione ad ADO.NET Stefano Del Furia Lab Informatica ISIS “Enrico Fermi” Bibbiena (AR) Mail: delfo@isisfermi.it Web: http://www.edudotnet.it
Il passato… ODBC (Open Database Connectivity) tentativo di creare uno standard per l’accesso ai dati orientato alla gestione di dati in forma tabellare sistema per accedere a dati provenienti da svariate piattaforme molto veloce, supporta molte delle istruzioni del linguaggio SQL si devono scrivere “svariate” righe di codice solo per stabilire una semplice connessione al database, figurarsi per realizzare una applicazione completa DAO (Data Access Objects) accedere in modo nativo al database Access (Jet-Engine) il modello ad oggetti è considerato da tutti orribile, lento ormai non quasi più usato da nessuno RDO (Remote Data Objects) evoluzione di ODBC e DAO considerato migliore di DAO, in quanto più veloce, e con il modello ad oggetti più raffinato più facile da usare di ODBC
Il passato… OLE DB successore di ODBC e tentativo, (riuscito), di avere uno standard comunicare con i database che non fossero solo in forma tabellare utilizza un provider (simile ad un driver di ODBC) molto veloce nell’accesso ai dati l’unico inconveniente che non è possibile svilupparci delle applicazioni in tutti i linguaggi (per esempio Visual Basic 6.0 è escluso) ADO (ActiveX Data Objects) doveva essere il nuovo RDO. è basato su OLE DB, il che fornisce un elevato livello di flessibilità 22 Modelli di accesso ai dati molte funzionalità come Cursori, Filtri da la possibilità di eseguire ordinamenti lato clienti ADO è più lento di RDO
Il presente: ADO.NET ADO .NET Oggetti connessi Oggetti disconnessi insieme di classi che forniscono tutte le funzionalità necessarie per l’accesso ai dati di un database e per la loro elaborazione in memoria. distingue concettualmente tra l’accesso ai dati del database e la loro elaborazione. il modello ad oggetti fornito è suddiviso in due parti, oggetti “connessi” ed oggetti “disconnessi”, due tipologie di oggetti sono completamente distinte Oggetti connessi vengono eseguite le operazioni di lettura e aggiornamento sul database progettati per interfacciarsi con uno specifico DBMS appartengono ad un determinato .NET Provider Oggetti disconnessi consentono la memorizzazione e l’elaborazione dei dati nella memoria del programma client completamente indipendenti dal database non sono mai in comunicazione con esso
Modalità di accesso ai dati
Modalità di accesso ai dati Connesso: Forward-only, read-only L’applicazione esegue una query, ottiene I risultati e li processa “immediatamente” Cursore “Firehose”, altissime prestazioni e bassissimo impatto in termini di risorse Oggetto DataReader Disconnesso L’applicazione esegue una query, recuperà I dati e li memorizza localmente per l’elaborazione Minimizza il tempo di connessione al database DataSet object The third main style is connected, scrollable cursor. ADO.NET does not currently support this style, though it may be supported in a future version. This can be implemented by using ADO.
Acceso Connesso ai Dati Nel modello client-server ogni cliente crea la propria connessione al DB all’avvio… …e la rilascia quando il programma termina. Il server deve mantenere una connessione attiva per ogni client… …anche se la connessione è utilizzata per una piccola frazione di tempo. Universal Data Access ActiveX Data Objects (ADO) was created to provide ‘Universal’ data access through a single set of objects idle Il modello connesso consuma risorse anche quando non sarebbe necessario busy idle Clients Connections Server
Acceso Connesso ai Dati Il DB server fornisce un “cursore” per mantenere il concetto di riga corrente Il client può leggere ( o scrivere) nella riga corrente mediante il cursore Ci si può spostare con il “commando” MoveNext Per supportare applicazioni GUI qualche database gestisce anche cursori ‘scrollabili’ (es. MovePrevious) Universal Data Access ActiveX Data Objects (ADO) was created to provide ‘Universal’ data access through a single set of objects Client rs Server Cursor Query Results Tables
Acceso Connesso ai Dati Vantaggi Svantaggi Si crea la connessione solo una volta Si impostano le proprietà della 'sessione‘ mediante la connessione Possibilità di effettuare lock e gestire la sicurezza se è previsto dal DB La prima riga di ogni query è 'immediatamente‘ disponibile Le connessioni che tengono “aperto” il database consumano risorse non molto scalabile Non adatto ad app Web utente non si “scollega” il numero di utenti non è prevedibile Non adatto ad app n-tier Le connessioni non possono essere passate tra i livelli
Accesso Disconnesso ai Dati Nel modello disconnesso, le connessioni al DB sono rilasciate appena possibile I Dati possono essere utilizzati anche dopo che la connessione è stata chiusa La connessione viene ri-creata solo per scrivere delle modifiche nel server Universal Data Access ActiveX Data Objects (ADO) was created to provide ‘Universal’ data access through a single set of objects idle Il modello disconnesso utilizza le risorse solo quando è necessario busy idle Clients Connections Server
Accesso Disconnesso ai Dati Tutti I dati sono inviati al client in un unica operazione Il risultato delle query viene mantenuto in memoria Tutte le risorse del server vengono rilasciate Il client manipola ed aggiorna i dati off-line Il client si ri-connette al database per scrivere gli aggiornamenti Utilizzo della chiave primaria per iidentificare il record corretto Universal Data Access ActiveX Data Objects (ADO) was created to provide ‘Universal’ data access through a single set of objects rs Client Server Cursor Query Results
Accesso Disconnesso ai Dati Vantaggi Svantaggi Un singolo DB server può gestire più utenti La manipolazione dei dati è più veloce e flessibile I dati non sono “vincolati” alla connessione facilità di passaggio tra I vari livelli o per scriverli su file Altamente indicato per app Web e n-tier Aprire e chiudere la connessione può essere “costoso” Recuperare molte righe può essere molto lento Necessita di potenza lato client Meno opzioni di lock
Panoramica su ADO.NET
Panoramica su ADO .NET Connection Data Provider DataSet DataRow DataTable DataAdapter DataReader Command
Organizzazione di ADO.NET Due grandi aree: .NET Data Providers Un provider per ogni sorgente dati Ogni provider ha il proprio namespace Supporti per la programmazione “connessa” Le Forms (WEB/WIN) fanno tra tramite tra DataSet e data source System.Data namespace Fornisce il modello ad oggetti del DataSet Supporto alla programmazione disconnessa Fornisce un modello di programmazione basato su interfacce per l’accesso “generico” ai managed providers
ADO.NET Managed Provider Managed Providers Applicazione ADO.NET Managed Provider SQL Managed Provider ADO Managed Provider OLE DB Provider SQL Server Database Database
Modelli di programmazione ADO.NET fornisce il supporto per i due distinti modelli di programmazione Connesso Usa gli oggetti connessi Command e DataReader Il DataReader è di tipo forward only e read-only Può eseguire aggiornamenti mediante l’oggetto Command Disconnesso Utilizza i DataSets I DataAdapters riempiono i DataSets con i dati ed eseguono le modifiche nel server I DataSets sono indipendenti dal Provider I DataSets sono memorizzati e serializzati come XML
.NET Data Providers I .NET Data Providers sono utilizzati per stabilire le connessioni con i DB Ecco alcuni Data Providers del .NET SQL Data Provider (System.Data.SqlClient) ODBC Data Provider (System.Data.Odbc) OLEDB Data Provider (System.Data.OleDb) MySql Data Provider (MySql.Data.MySqlClient) SqlConnection MySqlConnection OdbcConnection OleDbConnection SqlCommand MySqlCommand OdbcCommand OleDbCommand SqlDataReader MySqlDataReader OdbcDataReader OleDbDataReader SqlDataAdapter MySqlDataAdapter OdbcDataAdapter OleDbDataAdapter
Classi del .NET Data Provider Ogni Data Provider del .NET ha 4 classi Per esempio Il provider per SQL Server implementa SqlConnection The OLE DB data provider implements OleDbConnection Classe Descrizione Connection Stabilisce una connessione ad una specifica sorgente dati Command Esegue un comando verso la sorgente dati ha dei Parameters e può utilizzare una Transaction per una certa Connection DataReader Legge flussi di dati in modalità forward-only, read-only da una sorgente dati DataAdapter Popola un DataSets e risolve gli aggiornamenti con la sorgente dati
Riassumendo…. Il namespace System.Data.SqlClient: Classi per MS SQL Server 7 (e superiori) SqlConnection, SqlAdapter, ... Il namespace System.Data.OleDb Classi per il provider OLEDB OleDbConnection, OleDbAdapter, …
Accesso connesso
Effettuare una Connessione Connection Data Provider DataSet DataTable Command DataAdapter DataReader DataRow Transaction
La classe Connection Stabilisce una connessione al data source Simile alla ‘classica’ classe ADO Connection La stringa Connection controlla la connessione Simile alle precedenti stringhe di connessione La sintassi è dipendente dal provider Es: Sql Server “Server=localhost; Database=Pubs; Integrated Security=SSPI”
Proprietà e metodi Open() Close() BeginTransaction() ConnectionString State Open, Closed, Broken, ... Database Il database attualmente aperto Open() Close() BeginTransaction() Returns a Transaction object
L’oggetto Transaction Utilizzato per compiere operazioni di commit o rollback in una transazione o per fissare un “savepoint”
Proprietà e Metodi Connection IsolationLevel Commit() Rollback() L’oggetto Connection che ha iniziato la transazione IsolationLevel ReadCommited, Serializable, ... Commit() Opera un Commit nel data source Rollback() Opera un Rollback del data source Save() Imposta un savepoint durante una transazione
Code Example // Crea ed apre un oggetto SqlConnection SqlConnection cn = new SqlConnection(“Server=localhost; Integrated Security=true. . . . "); cn.Open(); // avvia una Transazione per quella connessione SqlTransaction trn = cn.BeginTransaction(); // fa quello che deve fare e… if(Pagamento(daConto, aConto, importo) == true) // ho pagato la bolletta trn.Commit(); else trn.Rollback(); cnn.Close();
Lettura dei dati DataSet DataTable Connection Data Provider DataAdapter DataRow DataReader Command
L’oggetto Command Una volta che si è creata una connesione si utilizza l’oggetto Command per eseguire istruzioni SQL o Stored Procedures
Proprietà e metodi CommandType CommandText Parameters ExecuteReader() Text, StoredProcedure e TableDirect CommandText query, nome di una tabella o di una stored procedure Parameters Collezione di Parametri ExecuteReader() Ritorna a DataReader ExecuteScalar() Ritorna un singolo valore scalare ExecuteNonQuery() Returna il numero di righe interessate Utilizzarlo per operazioni di update e delete
Esempio string cnStr =@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\AmiciAGG.mdb;Persist Security Info=False"; OleDbConnection cn = new OleDbConnection(cnStr); OleDbCommand cm = new OleDbCommand(); cm = new OleDbCommand("select nome from ragazzi where città = ?",cn); cm.Parameters.Add("@Raga", citta); cn.Open(); // fai qualcosa cn.Close();
L’oggetto DataReader Si utilizza il DataReader per leggere delle DataRows Cursore Forward only (Firehose)
Proprietà e Metodi Item Read() Collection of fields Read() Reads the next DataRow, returns false when no rows are left Accesso mediante indicizzatori, e Metodi [“Cognome”] [0] GetString() GetDecimal() GetBoolean()
Lavorare con i DataReaders ADO.NET non fornisce tutte le opzioni di cursore server-side che ci sono in ADO Es. KeySet Cursor, Static Cursor, Dynamic Cursor Il DataReader fornisce solamente un cursore foreward-only, read-only Utilizzarlo come fosse il classico Recordset ADO Non lasciare aperto il DataReaders più del necessario Usato correttamente i DataReaders sono efficienti Eseguire gli aggioramenti usando oggetti Command con il metodo ExecuteNonQuery() Per una gestione flessibile degli aggiornamenti lato client, utilizzare … DataSets e DataAdapters
Esempio string cnStr =@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\AmiciAGG.mdb;Persist Security Info=False"; OleDbConnection cn = new OleDbConnection(cnStr); OleDbCommand cm = new OleDbCommand(); cm = new OleDbCommand("select nome from ragazzi where città = ?",cn); cm.Parameters.Add("@LR", citta); cn.Open(); OleDbDataReader dr = cm.ExecuteReader(); while (dr.Read() == true) // dr.Read() torna false se non ci sono // più righe { Console.WriteLine(“Nome = “,dr["Nome"].ToString()); } cn.Close();
Accesso disconnesso
Modalità disconnessa Connection Data Provider DataReader DataRow DataSet DataTable Command DataAdapter DataRow UpdateCommand InsertCommand DeleteCommand SelectCommand
Esempio SqlDataAdapter adapter = new SqlDataAdapter(); SqlCommand delete = new SqlCommand("DeleteOrder",cnn); delete.CommandType=CommandType.StoredProcedure; delete.Parameters.Add("@OrderID",typeof(Int32)).SourceColumn="OrderID"; adapter.DeleteCommand = delete; SqlCommand insert = new SqlCommand("AddOrder",cnn); insert.CommandType=CommandType.StoredProcedure; insert.Parameters.Add("@OrderID",typeof(Int32)).SourceColumn="OrderID"; insert.Parameters.Add("@CustD",typeof(Int32)).SourceColumn="CustomerID"; insert.Parameters.Add("@Date",typeof(DateTime)).Value = DateTime.Now; adapter.InsertCommand = insert; SqlCommand update = new SqlCommand("UpdateOrder",cnn); update.CommandType=CommandType.StoredProcedure; update.Parameters.Add("@OrderID",typeof(Int32)).SourceColumn="OrderID"; update.Parameters.Add("@CustD",typeof(Int32)).SourceColumn="CustomerID"; adapter.UpdateCommand = update; adapter.Update(ordersTable);
Il DataSet Il DataSet fornisce una cache locale dei dati I DataSets sono sempre disconnessi Usare i DataSets quando: Si vuole fornire all’utente elevate capacità di manipolazione Scrolling, sorting, filtering Si vogliono passare i dati tra i vari “livelli” del programma Es. dal middle-tier al client window forms Si vuole che ADO.NET generi le istruzioni SQL per effettuare le modifiche ai dati del database
Il DataSet Usato come un recordset disconnesso Può contenere più DataTable Può memorizzare delle DataRelations Usate per relazionare le DataTable Mantiene le informazioni sullo schema Schema puà essere acquisiti a design-time design-time (typed DataSet) oppure a run-time
Proprietà Tables Relations EnforceConstraints Collezione di DataTable Collezione di Relation EnforceConstraints Abilita i vincoli del dataset
Metodi GetChanges Merge GetXml GetXmlSchema Ottiene un DataSet con solo le righe cambiate Merge Effettua il Merge di due DataSets GetXml Ottiene I dati dal DataSet in formato XML GetXmlSchema Ottiene lo schema in XML del DataSet
Il modello a oggetti del DataSet DataTableCollection DataTable DataColumnCollection DataColumn DataRowCollection DataRow ConstraintCollection Constraint DataRelationCollection DataRelation
Il DataTable Ogni oggetto DataTable rappresenta una tabella dati in un DataSet Creata da un DataAdapter Ai DataTables in un DataSet si accede usando la proprietà Tables del DataSet Collezione di DataTables Ogni DataTable ha: Una proprietà Columns Una proprietà Rows Una proprietà TableName Una proprietà PrimaryKey DataSet DataColumn DataTable DataRow DataColumn DataTable DataRow
L’oggetto DataTable Il DataTable contiene delle DataRow Può gestire i vincoli Unique, ForeignKey
Proprietà e metodi Columns Rows NewRow() Rows.Add() Select() Collezione di DataColumns Rows Collezione di DataRows NewRow() Crea una nuova riga Rows.Add() Aggiunge una DataRow alla collezione Rows Select() Crea un sottoinsieme della collezione Rows
Il DataColumn Ogni oggetto DataColumn descrive ona colonna in un DataTable Gli uggetti DataColumn descrivono lo schema delle tabelle Il DataAdapter può generare le necessarie DataColumns Le proprietà includono: ColumnName DataType AllowDBNull DataSet DataColumn DataTable DataRow DataColumn DataTable DataRow
Il DataRow Un oggetto DataRow fornisce l’accesso ad una riga di dati di un DataTable Gli oggetti DataRow di un DataTable contengono tutti i dati nel DataTable Si accede ai valori del DataRow fornendo: Numero della Colonna [0] Nome della Colonna [“Nome”] Oggetto DataColumn DataSet DataColumn DataTable DataRow DataColumn DataTable DataRow
Il DataRow Il DataRow contiene i dati Mantiene lo stato della riga Deleted, modified, inserted e detached Memorizza i valori originalie quelli nuovi
Proprietà RowState Item (indicizzatore) Added, Modified, Detached, Deleted and UnChanged Item (indicizzatore) Collezione di dati
DataSets & DataAdapters I DataSets non sono in grado di comunicare direttamente con i database DataSets sono completamente indipendenti dal data provider I DataAdapters forniscono il “ponte” tra i DataSet ed il data source
Il DataAdapter Contiene gli oggetti Command per operazioni di Select, Update, Delete ed Insert sui dati Gli oggetti Update, Delete ed Insert Command possono essere generati “automaticamente” dal CommandBuilder
Proprietà e Metodi SelectCommand UpdateCommand DeleteCommand Fill() Esegue il SelectCommand e memorizza I dati in un DataTable Update() Esegue il comando appropriato per aggiornare il Database
Comandi del DataAdapter Ci sono tre modi per creare gli oggetti xxxCommand del DataAdapter Usare l’oggetto CommandBuilder per costruire gli oggetti Command a run time Facile ma con un significativo overhead a run-time Limitato a SELECT su di una singola tabella Usare Visual Studio per generare il codice a design time Facile e senza nessun overhead a run-time Codificarlo a mano nel programma a design time Difficile ma con il più alto livello di controllo ed efficenza Nessuna limitazione
Popolare un DataSet I DataAdapters hanno la proprietà SelectCommand Specifica il comando SQL od il nome della stored procedure name per ottenere il result set private void Demo() { SqlDataAdapter da = new SqlDataAdapter("SELECT City FROM Authors","Server=localhost; Database=Pubs”); DataSet ds = new DataSet(); da.Fill( ds, "Authors" ); // Apre e chiude la connessione foreach ( DataRow dr in ds.Tables[ "Authors" ].Rows ) Console.WriteLine( dr[ "City" ] ); // Mostra le città }
Utilizzare l’oggetto DataSet Un DataSet può: Memorizzare i dati in più DataTable relazionate tra loro Modellare le relazioni tra più DataTable Gestire i vincoli Fornisce viste per ordinamenti e filtri sui dati
Aggiornare i dati
Seguire i cambiamenti del DataSet Una DataRow può mantenere, per ogni colonna, versioni multiple di un valore : DataRowVersion.Current Il valore corrente della colonna DataRowVersion.Original Il valore della colonna prima dei cambiamenti DataRowVersion.Proposed Il valore di una colonna durante un ciclo di BeginEdit / EndEdit DataRowVersion.Default Il valore di default per lo stato della riga (es. Original per le righe cancellate in quanto queste non hanno un valore Current)
Aggiungere/Cancellare Righe Aggiungere una riga ad una tabella con il metodo Add della proprietà Rows Il metodo NewRow crea una nuova riga ma non la aggiunge alla tabella Cancellare una riga con il metodo Delete Delete imposta lo stato della riga a Deleted, ma non la rimuove dalla tabella Per rimuoverla chiamare il metodo Remove della proprietà Rows Usare con cautela !!! Le righe rimosse non sono elaborate in fase di aggiornamento dal DataAdapter
Row State Ogni riga ha la proprietà RowState Added, Deleted, Detached, Modified, Unchanged Fornisce una specie di “storia” della riga Il metodo AcceptChanges effettua i cambiamenti RowState è impostato ad Unchanged La versione Current della riga viene copiata su quella Original Il metodo RejectChanges ripristina i valori originali La versione Original della riga viene copiata su quella Current
Esempio di Row State dr["au_lname"] = "Brown"; dr.AcceptChanges(); CURRENT ORIGINAL ROW STATE White White Unchanged Brown White Modified Brown Brown Unchanged dr["au_lname"] = "Brown"; dr.AcceptChanges();
Aggiornamenti via DataAdapter Gli aggiornamenti sono scritti nel database utilizzando il metodo Update del DataAdapter DataAdapter controlla il RowState di ogni riga Esegue le giuste azioni (insert, update o delete) in base allo stato della riga Usa gli oggetti Command assegnati mediante le proprietà InsertCommand, UpdateCommand e DeleteCommand DataRows nel DataTable DataAdapter Action RowState = Modified Use UPDATE command RowState = Unchanged Ignore RowState = Added Use INSERT command RowState = Modified Use UPDATE command RowState = Deleted Use DELETE command
Ottimizzare gli Aggiornamenti Sia i DataSets che i DataTables supportono il metodo GetChanges GetChanges senza argomenti Estrae tutte le righe nelle quali RowState non è Unchanged Più efficenza se si deve passarlo a qualcosa dsChanges = ds.GetChanges(); GetChanges con l’argomento RowState Estrae solo quelle righe con il RowState specificato Consente di controllare l’ordine di applicazione delle operazioni di insert, update e delete nel database changes = ds.GetChanges( DataRowState.Added ); Si può utilizzare il metodo Merge per applicare i risultati di un aggiornamento
Usare le Transazioni Esempio di un approccio “tutto-o-niente”: private void Save( DataSet ds, SqlDataAdapter da ) { SqlConnection con = da.InsertCommand.Connection; con.Open(); SqlTransaction tran = con.BeginTransaction(); da.InsertCommand.Transaction = tran; da.UpdateCommand.Transaction = tran; da.DeleteCommand.Transaction = tran; try da.Update( ds, "Authors" ); tran.Commit(); } catch tran.Rollback(); throw; finally con.Close(); Esempio di un approccio “tutto-o-niente”: La Connezzione è utilizzata per avviare la transazione I Comandi sono eseguiti nella transazione Vengono eseguiti gli aggiornamenti La Transazione è committed oppure rolled back
Dataset Tipizzati
DataSet Tipizzati Visual Studio 2005 può generare DataSets tipizzati Classi Custom derivate dalla classe DataSet Forniscono tutte le funzionalità del DataSet Possiede classi, metodi, eventi e proprietà specifiche per i dati contenuti Un DataSet tipizzato può contenere: Una classe chiamata Products derivata da DataTable Una classe chiamata ProductRow derivata da DataRow con le proprietà ProductID e Name
Vantaggi dei DataSets Tipizzzati I DataSets Tipizzati offrono numerosi vantaggi Controllo del tipo a Compile-time Funzionalità aggiuntive Per esempio un metodo FindByProductsID Codice più leggibile e manutenibile string s = productDataSet.Products[ 1 ].Name; invece di: string s = (string) productDataSet.Tables [ "Products" ].Rows[ 1 ].Item[ "Name" ]; Supporto all’IntelliSense
Creare un DataSet Tipizzato Visual Studio genera i DataSet tipizzati da uno schema di definizione XML (XSD) Visual Studio offre molti modi per generare lo schema XML Il modo più semplice è mediante l’ambiente di sviluppo
scriveteci a: labinfo@isisfermi.it {paolo, delfo}@edudotnet.it Quindi… Don’t worry… b.net !!! Visitate: www.edudotnet.it scriveteci a: labinfo@isisfermi.it supporto@edudotnet.it {paolo, delfo}@edudotnet.it