La gestione dell'asincrono è presente nel .NET Framework sin dalla versione 1.0, ma era limitata solamente allo sviluppo di HttpHandler in modalità asincrona, data la presenza dell'interfaccia IHttpAsyncHandler.
Prima di entrare nel dettaglio dell’utilizzo di tecniche asincrone in pagine ASP.NET, soffermiamoci sul perché una pagina web potrebbe agire in modalità asincrona.
Il framework, attraverso IIS (Internet Information Service), rende disponibili ad ogni applicazione ASP.NET un pool di thread che vengono associati, uno ad uno, ad ogni richiesta. Questo insieme di thread, che in ASP.NET 2.0 può arrivare fino a circa 5000 elementi, va gestito opportunamente di fronte ad applicazioni web abbastanza corpose. Per applicazioni corpose intendo quelle pagine web che effettuano delle operazioni che assegnano carichi elevati di lavoro alla CPU del server, come possono essere:- delle chiamate a web service remoti,
- delle richieste http,
- delle query complesse ad una base di dati.
Se una di queste operazioni, a causa di vari ritardi di rete, impiega un lasso di tempo che viene considerato inaccettabile, è buona regola inserirla in una pagina asincrona, in quanto il thread corrispondente alla richiesta, viene bloccato fin quando la medesima richiesta non arriva allo stato di termine. Mentre, se vengono applicate le tecniche asincrone messe a disposizione, è possibile far iniziare la richiesta da un thread, questo verrà quindi liberato e reinserito all’interno del pool di thread di ASP.NET una volta che l’operazione asincrona viene avviata nella pagina; la computazione sarà così spostata su un thread differente in modo da non bloccarne alcuno.
In grandi applicazioni web, in cui possono accedere migliaia di utenti, questo tipo di considerazioni non possono essere tralasciate.
Il supporto all’asincrono in ASP.NET 2.0
La versione 2.0 di ASP.NET aggiunge le caratteristiche di esecuzione asincrona direttamente alle pagine .aspx attraverso la valorizzazione della direttiva di pagina "Async".
Se, infatti, questo attributo è settato a true, la pagina verrà compilata a runtime implementando l'interfaccia IHttpAsyncHandler al posto dell'interfaccia IHttpHandler (perché ricordiamo che anche ogni pagina .aspx è in sostanza un HttpHandler e si comporta come tale).
Ora non resta che implementare all’interno della pagina le operazioni di inizio e fine richiesta.
ASP.NET ci aiuta mettendo a disposizione il metodo AddOnPreRenderCompleteAsync, proprio della classe Page, che da la possibilità di specificare due event handler, uno dove va implementato l'inizio della richiesta e uno dove ne va implementata la fine. Questi due event handler si posizionano subito dopo la fine dell’evento PreRender e subito prima dell’evento Render. Questa posizione specifica è detta "punto asincrono" ("async point").
Il primo dei due gestori d'evento, deve accettare questi 4 parametri:- object sender – l'oggetto che ha iniziato la richiesta
- EventArgs e – gli argomenti contenenti dati legati all'evento
- AsyncCallback cb – il delegato da richiamare una volta che la chiamata al metodo asincrono è stata completata
- object extraData – tutti quei dati addizionali utili a processare la richiesta
e restituire un oggetto di tipo IAsyncResult, che rappresenta il risultato della richiesta asincrona. Questo risultato viene poi utilizzato dal secondo event handler, che lo prende come parametro e che avrà il compito di terminare la richiesta asincrona.
Ecco un esempio di richiesta asincrona ad un web service remoto:
public partial class ServiceAsync : System.Web.UI.Page
{
localhost.HelloService service = null;
protected void Page_Load(object sender, EventArgs e)
{
AddOnPreRenderCompleteAsync(
new BeginEventHandler(Begin),
new EndEventHandler(End));
}
public IAsyncResult Begin(object sender, EventArgs e, AsyncCallback callback, object state)
{
Trace.Warn(String.Format("[Thread: {0}]Begin async", Thread.CurrentThread.ManagedThreadId));
service = new localhost.HelloService();
IAsyncResult result = service.BeginHelloWorld(callback, state);
return result;
}
public void End(IAsyncResult result)
{
Trace.Warn(String.Format("[Thread: {0}]End async", Thread.CurrentThread.ManagedThreadId));
Response.Write(service.EndHelloWorld(result));
}
}
Ma perché è stato scelto proprio l'evento PreRenderComplete come punto asincrono ?
La risposta è che l’architettura di ASP.NET 2.0, vede quel punto come l’unico punto in cui una o più operazioni asincrone possono essere revocate.
Come effettuare una richiesta asincrona ad un database Sql Server
L'utilizzo di una pagina asincrona per l'esecuzione di una query ad un database Sql Server diventa utile quando deve essere eseguita un operazione (quindi o una query testuale o una stored procedure) decisamente complicata che ritorni un numero considerevole di record.
A questo proposito, la classe SqlCommand, mette a disposizione due metodi: il metodo BeginExecuteReader, che inizia l'operazione sul db, e il metodo EndExecuteReader che la termina. Il secondo metodo torna un oggetto di tipo SqlDataReader che verrà poi utilizzato per effettuare il binding dei dati.
L'unico accorgimento da prendere è quello di aggiungere la segnatura "Asynchronous Processing=true;" alla stringa di connessione.
public partial class _Default : System.Web.UI.Page
{
private SqlConnection conn = null;
private SqlCommand cmd = null;
protected void Page_Load(object sender, EventArgs e)
{
AddOnPreRenderCompleteAsync(
new BeginEventHandler(Begin),
new EndEventHandler(End));
}
public IAsyncResult Begin(object sender, EventArgs e, AsyncCallback callback, object state)
{
Trace.Warn(String.Format("[Thread: {0}]Begin async", Thread.CurrentThread.ManagedThreadId));
conn = new SqlConnection(ConfigurationManager.ConnectionStrings["strConn"].ConnectionString);
cmd = new SqlCommand("SELECT * FROM Authors", conn);
cmd.CommandType = CommandType.Text;
conn.Open();
IAsyncResult result = cmd.BeginExecuteReader(callback, state);
return result;
}
public void End(IAsyncResult result)
{
using (SqlDataReader reader = cmd.EndExecuteReader(result))
{
if (reader != null)
{
if (!reader.Read())
return;
GridView1.DataSource = reader;
GridView1.DataBind();
reader.Close();
conn.Close();
Trace.Warn(String.Format("[Thread: {0}]End async", Thread.CurrentThread.ManagedThreadId));
}
}
}
}
Oltre a queste due implementazioni è possibile eseguire in maniera asincrona anche delle richieste HTTP, attraverso l'utilizzo della classe WebRequest (presente all'interno del namespace System.Net).
Conclusioni
Come avete potuto vedere, quindi, in ASP.NET 2.0 è stato inserito il supporto per l'asincrono all'interno di pagine .aspx; lo sviluppo di queste tecniche è stato reso quindi estremamente facile e flessibile data la presenza dell'attributo Async proprio di ogni pagina e del metodo AddOnPreRenderCompleteAsync().
Le cose si complicano leggermente, una volta che decidessimo di eseguire più richieste asincrone all'interno della stessa pagina, ma non è questo argomento di questa panoramica generale sul supporto asincrono di ASP.NET 2.
Link utili
Interfaccia IHttpAsyncHandler
Metodo AddOnPreRenderCompleteAsync
Delegato BeginEventHandler
Delegato EndEventHandler
|
|
|