in

DotNetSide

Dot Net South Italy Developers User Group

Articoli

Articoli pubblicati dagli iscritti a .netSide

Multiple Active Result Set

Autore: Michele Locuratolo (Mighell)

L’accesso ai dati da applicazioni .NET è una problematica molto sentita durante la progettazione e la realizzazione di applicazioni. Per “consumare” i dati contenuti in un Data Base ci sono molteplici strade, tutte corrette, ma che devono essere valutate in base al contesto ed al tipo di operazione da svolgere. Un' errata scelta può portare ad un inesorabile calo delle prestazioni dell’applicazione o ad un' elevata complessità nella scrittura del codice.
Alla base dell’accesso ai dati c’è un componente (o meglio una serie di componenti) denominata ADO.NET (ora giunto alla versione 2.0). Tra le tante novità che questa nuova versione introduce, ce n'è una estremamente comoda chiamata MARS: Multiple Active Result Set.
Vediamo di cosa si tratta.

Il DataReader ed il suo limite
Quando abbiamo bisogno di accedere a dei dati in modo veloce e leggero, il componente su cui va a cadere la nostra scelta è il DataReader. Esso, come sappiamo, è un oggetto molto comodo, leggero e veloce per leggere un flusso di dati ricevuto da un Data Base in modalità read-only, forward-only. Comodo si, ma dotato di una caratteristica che in alcune condizioni può diventare un vero limite: la connessione al Data Base viene mantenuta aperta finchè tutti i dati non sono stati letti.
Ci sono diversi casi in cui questo comportamento è una limitazione. Primo tra tutti, l’impossibilità di eseguire altre operazioni sul Data Base finchè non si è liberata la connessione, pena il sollevamento di una eccezione il cui messaggio è molto descrittivo: “There is alredy an open DataReader associated with this Connection which must be closed first” (Esiste già un DataReader aperto associato a questa Connessione che deve essere prima chiuso). Vi dice nulla?
Cerchiamo di capire insieme il problema (tutt’altro che raro) con un esempio.
Supponiamo di avere un Data Base alla base di un software per la gestione di un negozio di componenti elettronici. Quello che ci interessa sapere, interrogando la base dati, è quanti pezzi abbiamo venduto per ogni singolo componente. Cosa dovremmo fare (pensando in termini di interrogazioni al Data Base)?
La logica ci porta a ragionare come segue:

  • leggo i dati dalla tabella prodotti
  • per ogni prodotto (usando l'IDProdotto), eseguo una query che somma le quantità vendute nella tabella vendite

Per compiere questa operazione dobbiamo necessariamente inviare al Data Base due comandi distinti, ma sempre sulla stessa connessione. Cosa che, come abbiamo visto, non potremmo fare con il DataReader.
Chi ha sviluppato applicazioni che accedono ai dati, sarà prima o poi incappato in questo limite dovendo, di volta in volta, escogitare una soluzione (spesso si usava creare due connessioni distinte al fine di poter eseguire due comandi diversi).

MARS
Compreso il limite del Data Reader, vediamo come il Multiple Active Reslt Set ci vene in aiuto.
Innanzitutto dobbiamo specificare che MARS funziona con ADO.Net 2.0 e SQL Server 2005. Quello che ci permette di fare è di inviare al Data Base comandi distinti attraverso la stessa connessione.
Come al solito, un esempio vale più di 1000 parole:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;   

namespace MARS {
    class Program {
        static void Main( string[] args ) {
            Console.WriteLine( "*** Multiple Active Result Sets ***\r\n" );
            TestMars();
        }

        /// <summary>
        /// Test funzionalità MARS
        /// </summary>
        private static void TestMars() {
            SqlConnection myConnection = new SqlConnection();
            //Va impostato il parametro MultipleActiveResultSets sulla stringa di connessione
            myConnection.ConnectionString = "Data Source=.\\SQLEXPRESS;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=myStore;MultipleActiveResultSets=True";
            SqlCommand myCommand = myConnection.CreateCommand();
            myCommand.CommandType = CommandType.Text;
            myCommand.CommandText = "Select * from tblProduct";
            myConnection.Open();
            SqlDataReader myDataReader = myCommand.ExecuteReader();
            while ( myDataReader.Read() ) {
                SqlCommand mySecondCommand = myConnection.CreateCommand();
                mySecondCommand.CommandType = CommandType.Text;
                mySecondCommand.CommandText = "Select Sum(quantita) as Venduto from tblVendite where IDProduct = @IDProduct";
                SqlParameter parameter = mySecondCommand.CreateParameter();
                parameter.ParameterName = "@IDProduct";
                parameter.Value = (int)myDataReader["IDProduct"];
                mySecondCommand.Parameters.Add( parameter );
                //Eseguo il secondo comando sulla stessa connessione
                int QuantitaVenduta = (int)mySecondCommand.ExecuteScalar();
                Console.WriteLine( "Prodotto: {0}\r\nVendite: {1}\r\n-----\r\n", (string)myDataReader["NomeProdotto"], QuantitaVenduta.ToString() );
            }
            myConnection.Close();
        }
    }
}

Una delle prime cose da ricordare è che, per usare MARS, la connessione deve essere predisposta e lo si fa impostando il parametro MultipleActiveResultSets=true nella stringa di connessione. La cosa va fatta solo per le connessioni che realmente necessitano di questa funzionalità, pena un decadimento delle prestazioni.
Come poi si vede dal codice, viene prima eseguita una selezione su tutti i record della tabella tblProducts il cui risultato è un DataReader. Eseguendo un ciclo su tutti i record, viene recuperato l’ID del prodotto che viene passato alla seconda query che effettua il conteggio dei quantitativi venduti.
Il risultato sarà:

*** Multiple Active Result Sets ***
Prodotto: Chiavetta USB 1GB
Vendite: 42
-----
Titolo: Compact Flash 4GB
Vendite: 29
-----

Conclusioni
Questa nuova funzionalità, al contrario di quanto si possa pensare, non introduce miglioramenti di performance nell’interrogazone al Data Base....anzi! Impostare a true il parametro MultipleActiveResultSets nella stringa di connessione anche quando non serve, potrebbe avere impatti negativi. Come detto in precedenza, prima di MARS era comunque possibile ottenere lo stesso rsultato ma non in modo così immediato (ossia usando almeno due connessioni od oggetti più pesanti come il DataSet). Nel caso delle 2 connessioni, non è da trascurare l’eventuale risparmio sull’aquisto di CALS in ambienti client/server.
Il Multiple Active Result Set deve essere considerato solo una comodità per lo sviluppatore in quanto permette di ottenere un risultato usando meno codice.

Only published comments... Sep 14 2006, 11:26 AM by DotNetSide Staff
Filed under:

Comments

 

Mighell's blog said:

Ho appena pubblicato un mio articolo su MARS: Multiple Active Result Sets. Ormai non &#232; esattamente una

September 14, 2006 12:52 PM
 

wolfSharp said:

Grazie Michele, mi hai fatto scoprire una cosa nuova. Ho sempre sviluppato con la versione 1.1 del Framework e quindi non conoscevo MARS.

Vorrei però far notare che in un normale ambiente di produzione non si ricorrerebbe mai a questa tecnica in quanto si affiderebbe il calcolo unicamente ad una stored procedure, migliorando le prestazioni ed il mantenimento del codice.

Enzo

January 26, 2007 11:08 PM
Powered by Community Server (Commercial Edition), by Telligent Systems