Contesto: devo lanciare un'operazione che può durare molto tempo eseguita da un metodo sincrono che non torna eventi (legacy). Devo fornire un feedback all'utente (una sorta di pagina clessidra) e, a lavoro terminato, mostrare il risultato.
Soluzione adottata: creo una pagina per iniziare l'operazione e configurarne i parametri (pagina Start), una autorefreshante per comunicare che il lavoro è in corso (pagina Wait) e una pagina per mostrare i risultati (pagina Results).
La pagina Start:
- mette in sessione un flag per indicare che il calcolo è in corso
- istanzia un delegate per richiamare il metodo sincrono con un nuovo thread;
- lancia il nuovo thread passando eventuali parametri del metodo, un riferimento al metodo di callback e un riferimento al caller stesso
- carica la pagina Wait
Il caller del MetodoLungo:
- a lavoro completato richiama il metodo di callback sulla pagina Start
Il metodo di callback (su Start):
- riaggancia il chiamante
- recupera il risultato del calcolo usando EndInvoke sul chiamante
- mette il risultato in sessione
- setta il flag in sessione per indicare che il calcolo è terminato
La pagina Wait:
- ha un tag meta (magari inserito da codice) per fare il refresh automatico
- verifica il flag in sessione per sapere quando passare alla pagina dei risultati
La pagina Results:
- recupera il risultato dalla sessione e lo mostra
Codice:
pagina Start:
public delegate TipoRisultatoCalcoloLungo MetodoLungoAsyncCaller(TipoParametro1MetodoLungo par1, TipoParametro2MetodoLungo par2);
public partial class StartPage
{
...
void Toolbar1_ButtonClick(object sender, ToolbarEventArgs ea)
{
Session[SessionConstants.SCAN_IN_PROGRESS] = true;
// Create the delegate.
MetodoLungoAsyncCaller caller = new MetodoLungoAsyncCaller(MetodoLungo);
// Initiate the asychronous call. Include an AsyncCallback
// delegate representing the callback method, and the data
// needed to call EndInvoke.
IAsyncResult result = caller.BeginInvoke(valorePar1, valorePar2,
new AsyncCallback(CallbackMethod),
caller);
// goto to "hourglass" page
Response.Redirect(WebFormsConstants.WAIT_ASPX);
}
...
// Callback method
public void CallbackMethod(IAsyncResult ar)
{
// Retrieve the delegate.
MetodoLungoAsyncCaller caller = (MetodoLungoAsyncCaller)ar.AsyncState;
// Call EndInvoke to retrieve the results.
TipoRisultatoCalcoloLungo risultato = caller.EndInvoke(ar);
Session[SessionConstants.RISULTATO] = risultato;
// set flag in session to comunicate scan completed
Session[SessionConstants.SCAN_IN_PROGRESS] = false;
}
...
}
pagina Wait:
protected void Page_Load(object sender, EventArgs e)
{
HtmlMeta meta = new HtmlMeta();
meta.HttpEquiv = "refresh";
meta.Content = "10";
Header.Controls.Add(meta);
if ((Boolean)Session[SessionConstants.SCAN_IN_PROGRESS] == false)
Response.Redirect(WebFormsConstants.RESULT_ASPX);
Message1.Text = "In Progress";
}