ASP.NET 2.0 Async Pages
Utilizzando l'attributo Async="true" su una pagina aspx, la classe generata dietro le quinte implementa anche IHttpAsyncHandler rendendo molto più semplice l'utilizzo di tecniche asincrone per l'accesso alle informazioni (ADO.NET 2.0 implementa metodi asincroni per l'accesso al DB). Non occorre più scrivere a mano una classe che implementi IHttpAsyncHandler perdendo le funzionalità classiche come User Control, Eventi, lifecycle etc (e nella 2.0 Master Page, Theme etc). E' vero che anche in ASP.NET 1.x si può ricorrere a trucchetti per utilizzare un IHttpAsyncHandler che esegua le operazioni asincorne per poi eseguire una Server.Transfer verso la pagina che invierà la response al client, oppure gestire le chiamate asincrone dal global.asax per poi lasciar proseguire la richiesta verso le pagine che invieranno i risultati, ma è altrettanto vero che con la 2.0 diventa tutto molto più semplice. Si possono usare anche le PageTask per tutte le situazioni in cui una chiamata asincrona mal riuscita non debba invalidare tutta la pagina.
Questo un estratto da una demo che faremo alla DevCon 2005 dove dedicheremo quasi un'ora alle gestione asincrona di risorse per rendere molto (molto vuol dire moltissimo) più scalabili le applicazioni che eseguano richieste a risorse remote (dove risorse vuol dire anche database).
Questa pagina invoca un web service in modo asincrono seguendo il nuovo pattern MethodAsync; Nel sorgente sono commentate le righe del pattern Begin/End. N.B. utilizziamo una master page che espone un metodo per eseguire trace condiviso di quando succede ai thread.
<%
@ Page Language="C#" Async ="true" MasterPageFile="~/MasterPage.master" %>
<%
@ MasterType VirtualPath="~/MasterPage.master" %>
<
script runat="server">
void Page_Load() {
Master.AddTraceMessage(
"SalvaAgente 2.0 Async");
SalesmanManagerWS.
SalesmanManagerWS ws = new SalesmanManagerWS.SalesmanManagerWS();
// Nuova riga
ws.SalvaAgenteCompleted +=
this.OnSalvaAgenteCompleted; // Non occre definire il delegate (fa lui)
// Non servono più
//IAsyncResult ar = ws.BeginSalvaAgente("robertob", "RobertoBrunetti", AgenteSalvato, ws);
//ar.AsyncWaitHandle.WaitOne();
ws.SalvaAgenteAsync(
"Robertob", "RobertoBrunetti");
Master.AddTraceMessage(
"SalvaAgente 2.0 Finito");
}
//Definizione più conforme alla normale gestione eventi
// private void AgenteSalvato(IAsyncResult ar)
private void OnSalvaAgenteCompleted(object sender, SalesmanManagerWS.SalvaAgenteCompletedEventArgs e)
{
// N.B. Typed Result (SalvaAgenteCompletedEventArgs)
// N.B. Il thread che esegue questo codice non è detto che sia lo stesso del Page_Load
Master.AddTraceMessage(
"SalvaAgente 2.0 Completed");
// Via sto casino
// SalesmanManagerWS.SalesmanManagerWS ws = (SalesmanManagerWS.SalesmanManagerWS)ar.AsyncState;
// bool ris = ws.EndSalvaAgente(ar);
bool ris = e.Result;
Master.AddTraceMessage(
"Risultato " + ris.ToString());
}
</
script>
Dietro le quinte viene generata questa classe che come si puà notare implementa IHttpAsyncHandler oltre a al classico IHttpHandler
public class _09chiamaunws20eventbased_aspx : Page, IHttpAsyncHandler, IHttpHandler
{
// Methods
public _09chiamaunws20eventbased_aspx();
private void __BuildControlTree(_09chiamaunws20eventbased_aspx __ctrl);
public virtual IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object data);
public virtual void EndProcessRequest(IAsyncResult ar);
protected override void FrameworkInitialize();
public override int GetTypeHashCode();
private void OnSalvaAgenteCompleted(object sender, SalvaAgenteCompletedEventArgs e);
private void Page_Load();
public override void ProcessRequest(HttpContext context);
// Properties
protected global_asax ApplicationInstance { get; }
public masterpage_master Master { get; }
protected DefaultProfile Profile { get; }
// Fields
private static object __fileDependencies;
private static bool __initialized;
}