ADO .NET 2.0: what’s new
Andrea Saltarello
Software Architect – Managed Designs S.r.l.
http://blogs.ugidotnet.org/pape
Sponsor
Parliamo di…
•
•
•
•
Architettura ADO .NET 2.0
System.Data: what’s new
SqlClient enhancements
M.A.R.S.
Agenda – Non parliamo di…
ObjectSpaces
• Inizialmente annunciato come parte di ADO
.NET 2.0…
• E’ stato poi “agganciato” al rilascio di WinFS
(Longhorn)…
• Ma WinFS è stato “tagliato” da Longhorn e
posticipato a data da definirsi…
• Insomma… non ne parliamo 
Prerequisiti per ADO .NET
2.0
• Non necessitano di MDAC:
• Classi base, comuni (System.Data.Common) e
disconnesse
• .NET managed provider per SQL Server e
Oracle
• Usano MDAC, senza particolari
requisiti:
• Managed provider OLEDB e ODBC
• Vanno bene le versioni 2.6, 2.7, 2.8 o… 9.0
Common Provider Model
• ADO.NET v1.0/1.1 è basato su
alcune interfacce
• E’ problematico scrivere codice indipendente
dalla base dati
• ADO .NET 2.0 è basato su classi
base condivise dai provider
• E’ una estensione, non introduce incompatibilità
• La sintassi SQL è comunque specifica per la
base dati!
• Architettura basata sul pattern Factory
ADO .NET 2: Architettura
IDb* interfaces (es: IDbConnection)
Db* abstract base classes (es: DbConnection)
Layer
ProviderIndependent
Db*Base implementation classes
Sql
OleDb
ODBC
Oracle
3rd
3rd
Party 1
Party 2
Layer
Providerspecific
Classi Provider-independent
Sono definite nel namespace
System.Data.Common
– ad esempio:
DbCommand
DbCommandBuilder
DbConnection
DataAdapter
DbDataAdapter
DbDataReader
DbParameter
DbParameterCollection DbTransaction
DbProviderFactory DbProviderFactories
DbException
Usare una Provider Factory
• Importare il Namespace:
using System.Data.Common
• Creare l’istanza della Factory:
static DbProviderFactory factory =
DbProviderFactories.GetFactory("provider-name")
• Creare le istanze degli oggetti:
DbConnection con = factory.CreateConnection()
DbCommand cmd = con.CreateCommand()
Provider Enumeration
• Ogni provider ha un nome invariante
– Per esempio: "System.Data.SqlClient", "System.Data.OracleClient"
• Ottenere la lista delle provider factory
installate
DataTable dt =
DbProviderFactories.GetFactoryClasses()
DbProviderFactory factory =
DbProviderFactories.GetFactory(dt.Rows[x])
– ... o ...
DbProviderFactory factory =
DbProviderFactories.GetFactory(
dt.Select("InvariantName='System.Data.SqlClient'"
) [0]["InvariantName"].ToString());
Provider Enumeration
usando una Provider Factory
Performance Improvements
• Indicizzazione interna delle righe. I tempi
di:
– Insert e delete crescono logaritmicamente
– update rimangono quasi costanti
• Serializzazione binaria
– I DataSet v1.x erano sempre serializzati in
XML
• Vantaggioso per lo scambio dati, ma penalizzante
per le performance
– La v2.0 supporta la serializzazione binaria
• È più veloce ed occupa meno spazio
• Basta usare: DataSet.RemotingFormat =
SerializationFormat.Binary
Performance inserimento
righe
4000
3500
3000
Time in Milliseconds
2500
2000
Everet t
Whidbey
1500
1000
500
0
0
10000
20000
30000
40000
Number of Rows
50000
60000
70000
DataRow Insertion Perf Improvement
End-to-End Time (lower is
better)
Binary vs. XML Serialization
Xml
Binary
100
1000
10000
Row s in DataSet
Incrementi fino ad 80 x
100000
Popolare un DataSet
• DataAdapter.Fill(DataSet, "table-name")
– Nuovo: proprietà DataAdapter.FillLoadOption e
AcceptChangesDuringUpdate
• Nuovo: metodo DataSet.Load
– Load(DataReader [, load-option] [, tablesarray])
• Nuovo: LoadOption enumeration
– PreserveCurrentValues | UpdateCurrentValues
| OverwriteRow
LoadOption Enumeration
RowState of
Existing Row
PreserveCurrentValues
(the default value)
UpdateCurrentValues
OverwriteRow
Added
Current = Existing
Original = Incoming
RowState = Modified
Current = Incoming
Original = Existing
RowState = Added
Current = Incoming
Original = Incoming
RowState = Unchanged
Modified
Current = Existing
Original = Incoming
RowState = Modified
Current = Incoming
Original = Existing
RowState = Modified
Current = Incoming
Original = Incoming
RowState = Unchanged
Deleted
Current = Existing
Original = Incoming
RowState = Deleted
* Undo Delete
Current = Incoming
Original = Existing
RowState = Modified
* Undo Delete
Current = Incoming
Original = Incoming
RowState = Unchanged
Unchanged
Current = Incoming
Original = Incoming
RowState = Unchanged
Current = Incoming
Original = Existing
* if new value = existing value:
RowState = Unchanged
* else:
RowState = Modified
Current = Incoming
Original = Incoming
RowState = Unchanged
No matching
existing row in the
table
Current = Incoming
Original = Incoming
RowState = Unchanged
Current = Incoming
Original = Existing
RowState = Added
Current = Incoming
Original = Incoming
RowState = Unchanged
Nuove feature
• RowState modificabile
– Nuovi metodi: DataRow.SetAdded e
DataRow.SetModified
• Metodo DataSet.GetDataReader
– Restituisce un DataTableReader
– E’ possibile specificare quali tabelle includere
Popolare un DataSet con un DataReader
e usare un DataTableReader.
DataTable “stand-alone”
• Operazioni tipiche del DataSet supportate anche
per le DataTable:
– ReadXml, ReadXmlSchema, WriteXml,
WriteXmlSchema, Clear, Clone, Copy, Merge,
GetChanges
• La classe DataTable ora supporta la
serializzazione:
– Perchè implementa IXmlSerializable
– E’ possibile restituire una istanza di DataTable mediante
Web Service o Remoting
Popolare ed usare DataTable
• DataAdapter.Fill(DataTable)
• DataAdapter.Fill(DataTable[ ], …)
– Permette di selezionare un sottoinsieme di righe
• DataAdapter.Update(DataTable)
• DataTable.Load(DataReader [, load-option]
[, FillErrorEventHandler])
– Nuovi metodi: BeginLoadData, Load, EndLoadData
• DataTable.GetDataReader method
– Ottiene uno stream da una DataTable
Update Batch
• In 1.1, gli update dei DataSet sono eseguiti
riga per riga
• Il batch update riduce round-trip lungo la
rete
• DataAdapter.UpdateBatchSize =
batch_size
• Funziona all’interno di transazioni
• Funziona con SQL Server 7.0, 2000, 2005
• Disponibile anche per il provider
OracleClient
DataTable “stand-alone”
Comandi asincroni
• Ideali per eseguire molteplici query
• Il “solito” Async Pattern: Beginxxx e
Endxxx
• Supporta Polling, Wait e Callback
• Non dovrebbe essere usato in abbinamento
a MARS
– usate una connessione per ogni Command
• Aggiungere "async=true" alla connection
string
Esecuzione sincrona
Application
Rowset 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Connection
Database1
Latency 3 secs
Connection
Database2
Latency 8 secs
Connection
Database3
Latency 5 secs
Rowset 2
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Rowset 3
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Tempo per visualizzare i dati:
~ 16
3 secs
11
secs
Esecuzione asincrona
Application
Rowset 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Connection1
Database1
Latency 3 secs
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Connection2
Database2
Latency 8 secs
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Connection3
Database3
Latency 5 secs
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Rowset 2
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Rowset 3
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Data 1
Tempo per visualizzare i dati:
~ 8 secs
Esecuzione asincrona:
Polling Model
• Avviare un comando asincrono:
Dim result As IAsyncResult =
MyCommand.BeginExecuteReader()
• Aspettare fino al termine della
esecuzione:
While Not result.IsCompleted
‘Codice da eseguire
End While
• Recuperare i risultati:
Dim reader As SqlDataReader =
MyCommand.EndExecuteReader(result )
Esecuzione asincrona: Wait
(All) Model
• Avviare uno o più comandi asincroni:
Dim resultx As IAsyncResult =
MyCommand.BeginExecuteReader()
• Attendere il completamento di tutti i
comandi:
WaitHandle.WaitAll(New WaitHandle()
{result1.AsyncWaitHandle,
result2.AsyncWaitHandle,
result3.AsyncWaitHandle}, timeout - ms, True)
• Recuperare i risultati:
Dim reader As SqlDataReader =
MyCommand.EndExecuteReader(resultx)
• Ideale per le Web Application ASP.NET
Esecuzione asincrona: Wait
(Any) Model
• Avviare uno o più comandi asincroni come array di
istanze di IAsyncResult:
Dim resultx As IAsyncResult =
MyCommand.BeginExecuteReader()
• Attendere il completamento di ogni comando:
Dim i As Integer
For i = 0 To result_array.Length
index = WaitHandle.WaitAny(result_array,
timeout, true)
Select Case index
Case 0
Dim reader As SqlDataReader =
MyCommand.EndExecuteReader(resultx)
...
End Select
End For
Esecuzione asincrona:
Callback Model
• Avviare l’esecuzione, specificando la
funzione di callback e il command come
AsyncState:
MyCommand.BeginExecuteReader(new
AsyncCallback(MyCallback), cmd)
• Creare il gestore del callback:
Sub MyCallback(ByVal result As IAsyncResult)
Dim cmd As SqlCommand =
DirectCast(result.AsyncState, SqlCommand)
Dim reader As SqlDataReader =
cmd.EndExecuteReader(result)
End Sub
Esecuzione di comandi asincroni
DataReader: Paging
• Il paging? C’est plus facile, col metodo
ExecutePageReader
• Accetta l’indice della prima riga e il numero di righe da restituire
• Non garantisce consistenza
• Senza il supporto di una transazione, alcune righe potrebbero
essere saltate o ripetute
• Moooolto più veloce che leggere tutte le righe
ed ignorare quelle non desiderate
– E’ comunque preferibile eseguire una query “mirata”
Troppo bello per essere vero? Lo pensa anche
Microsoft, infatti ha rimosso il metodo nella
beta2
Multiple Active Result Sets
(MARS)
• (Ri)Conoscete questo messaggio? 
– System.InvalidOperationException: There is
already an open DataReader associated with
this Connection which must be closed first.
Multiple Active Results Sets
(MARS)
• Mantiene disponibile una connessione
quando apriamo un SqlDataReader al fine
di poter:
– Eseguire un’altra query per ottenere un
DataReader/XmlReader
– Eseguire comandi DML
• Possono essere attivi differenti result set
contemporaeamente
– alternare fetch ad ogni reader
– Alternare query che non restituiscono reader
Alternare Results Set
• Scenario tipico:
– Recuperare la lista dei clienti e scorrerla
– Per ogni cliente, recuperare la lista degli ordini
– Per ogni ordine, recuperarne il dettaglio
• Nella v1, per ottenere ciò usando dei reader
erano necessarie differenti connessioni
• Mediante MARS, è sufficiente una sola
connessione se:
– I dati risiedono nello stesso database
– Usiamo SQL Server 2005/MDAC9
Interleaved Results Sets
Example
Dim parentReader As DataReader =
Command1.ExecuteReader()
While parentReader.Read()
' process parent row data here
' then get rowset from child table
Command2.Parameters("@id").Value =
parentReader("id")
Dim childReader As DataReader =
Command2.ExecuteReader()
' process child rows here
childReader.Close()
End While
parentReader.Close()
Links
http://www.ugidotnet.org
http://forum.ugidotnet.org
http://mobile.ugidotnet.org
Scarica

ADO .NET 2.0: what`s new