370 likes | 480 Vues
Discover the latest features and improvements in ADO.NET 2.0, including System.Data enhancements, SqlClient enhancements, and ObjectSpaces. Explore performance improvements, new features, and best practices. Dive into the details with a comprehensive guide.
E N D
ADO .NET 2.0: what’s new Andrea Saltarello Software Architect – Managed Designs S.r.l. http://blogs.ugidotnet.org/pape
Parliamo di… • Architettura ADO .NET 2.0 • System.Data: what’s new • SqlClient enhancements • M.A.R.S.
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 ObjectSpaces Agenda – Non parliamo di…
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) Layer Provider-Independent Db* abstract base classes (es: DbConnection) Db*Base implementation classes Sql OleDb ODBC Oracle 3rd Party 1 3rd Party 2 Layer Provider-specific
Classi Provider-independent Sono definite nel namespace System.Data.Common • ad esempio:
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
Binary vs. XML Serialization Incrementi fino ad 80 x
Popolare un DataSet • DataAdapter.Fill(DataSet, "table-name") • Nuovo: proprietà DataAdapter.FillLoadOption e AcceptChangesDuringUpdate • Nuovo: metodo DataSet.Load • Load(DataReader [, load-option] [, tables-array]) • Nuovo: LoadOption enumeration • PreserveCurrentValues | UpdateCurrentValues | OverwriteRow
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
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
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 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 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 Esecuzione sincrona Application Rowset 1 Database1 Latency 3 secs Connection Rowset 2 Database2 Latency 8 secs Connection Rowset 3 Database3 Latency 5 secs Connection Tempo per visualizzare i dati: ~ 16 secs ~ 11 secs ~ 3 secs
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 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 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 Connection1 Connection2 Connection3 Esecuzione asincrona Application Database1 Latency 3 secs Rowset 1 Database2 Latency 8 secs Rowset 2 Rowset 3 Database3 Latency 5 secs 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
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