Introduzione a ADO.NET

annuncio pubblicitario
Introduzione ad ADO.NET
Stefano Del Furia
Lab Informatica ISIS “Enrico Fermi” Bibbiena (AR)
Mail: [email protected]
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
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
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.
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)
Client
rs
Server
Cursor
Query Results
Tables
Acceso Connesso ai Dati
Vantaggi
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
Svantaggi
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
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
Client
rs
Cursor
Server
Query Results
Accesso Disconnesso ai Dati
Vantaggi
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
Svantaggi
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
DataSet
DataRow
Data Provider
DataTable
DataAdapter
DataReader
Command
Connection
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
Managed Providers
Applicazione
ADO.NET Managed Provider
OLE DB
Provider
SQL Server
Database
SQL Managed Provider
Database
ADO Managed Provider
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
SqlCommand
SqlDataReader
SqlDataAdapter
OdbcConnection
MySqlCommand
OleDbConnection
OdbcCommand
MySqlDataReader
MySqlDataAdapter
OleDbCommand
OdbcDataReader
OdbcDataAdapter
OleDbDataReader
OleDbDataAdapter
Classi del .NET Data Provider
Ogni Data Provider del .NET ha 4 classi
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
Per esempio
Il provider per SQL Server implementa SqlConnection
The OLE DB data provider implements OleDbConnection
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
DataSet
DataRow
Data Provider
DataTable
DataAdapter
DataReader
Command
Transaction
Connection
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
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
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
DataRow
Data Provider
DataTable
DataAdapter
DataReader
Command
Connection
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
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
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 forewardonly, 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
DataSet
DataRow
Data Provider
DataTable
DataRow
DataAdapter
DataReader
Command
UpdateCommand
InsertCommand
DeleteCommand
SelectCommand
Connection
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
Collezione di DataTable
Relations
Collezione di Relation
EnforceConstraints
Abilita i vincoli del dataset
Metodi
GetChanges
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
DataSet
DataTableCollection
DataRelationCollection
DataTable
DataColumnCollection
DataColumn
DataRowCollection
DataRow
ConstraintCollection
Constraint
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
DataSet
DataTable
DataColumn
DataRow
Collezione di DataTables
Ogni DataTable ha:
Una proprietà Columns
Una proprietà Rows
Una proprietà TableName
Una proprietà PrimaryKey
DataTable
DataRow
DataColumn
L’oggetto DataTable
Il DataTable contiene delle DataRow
Può gestire i vincoli
Unique, ForeignKey
Proprietà e metodi
Columns
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
DataTable
DataColumn
DataRow
DataTable
DataRow
DataColumn
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
DataSet
DataTable
DataRow
Si accede ai valori del
DataRow fornendo:
Numero della Colonna [0]
Nome della Colonna
[“Nome”]
Oggetto DataColumn
DataColumn
DataTable
DataRow
DataColumn
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
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
UpdateCommand
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
Limitato a SELECT su di una singola tabella
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
RowState è impostato ad Unchanged
La versione Original della riga viene copiata su quella
Current
Esempio di Row State
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”:
La Connezzione
è utilizzata per
avviare la
transazione
I Comandi sono
eseguiti nella
transazione
Vengono eseguiti
gli aggiornamenti
La Transazione è
committed oppure
rolled back
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();
}
}
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
Quindi…
Don’t worry…
b.net !!!
Visitate:
www.edudotnet.it
scriveteci a: [email protected]
[email protected]
{paolo, delfo}@edudotnet.it
Scarica