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";
        }