Belius' Blog

Il blog di Simone Belia
posts - 16, comments - 335, trackbacks - 31

Operazioni Lunghe con ASP.NET e AJAX

Diverse volte mi è capitato di dover eseguire operazioni batch abbastanza lunghe su delle Web Application che duravano anche qualche minuto.

L'utente che quindi premeva il fatidico pulsante "Start" non poteva utilizzare nessuna altra sezione dell'applicazione poichè avrebbe interroto inevitabilmente l'operazione in corso.
Dopo tante richieste da parte di questi utenti ho deciso di documentarmi meglio e, leggendo di qua e prendendo spunto di la sono riuscito a creare un paio di classi che permettono di eseguire le operazioni lunghe "dietro le quinte" permettendo agli utenti di continuare a lavorare.

Se poi consideriamo il fatto che AJAX ci sta dando una grossa mano per rendere più "vere" le Web Application allora siamo a cavallo. :-)

 L'implementazione si riassume in due classi Task.cs e SampleTask.cs.

TASK.CS

 public abstract class Task
 {
     
protected bool _running = false;
    public bool 
Running
    {
        
get return _running}
    }

    
protected Exception _exception = null;
    public 
Exception Exception
    {
        
get return _exception}
    }

    
public void StartTask()
    {
        
lock (this)
        {
            
if (!_running)
            {
                _running 
= true;
                
Thread tTask = new Thread(new ThreadStart(Process));
                
tTask.Start();
            
}
        }
    }

    
public virtual void StopTask()
    {
        
this._running = false;
    
}

    
public virtual void Process() { }
    
}

Questa classe è astratta poichè fornisce l'implementazione base di tutte le eventuali operazioni che richiedo un certo tempo di esecuzione.

SAMPLETASK.CS

public class 
SampleTask : Task
{
    
public SampleTask() { }

    
private int _step 0;
    public int 
Step
    {
        
get return _step}
    }
    
    
public override void Process()
    {
        
try
        
{
            
for (int 0i < 100i++)
            {
                
if (this.Running)
                {
                    _step 
i;
                    
System.Threading.Thread.Sleep(200);

                    
//if (i>20)
                    //    throw new ArgumentException("Exception occurred");
                
}
                
else
                
{
                    
break;
                
}
            }
        }
        
catch (Exception e)
        {
            _exception 
e;
        
}
        
finally
        
{
            _running 
= false;
        
}
    }

    
public override void StopTask()
    {
        
base.StopTask();
    
}

}


Questa classe invece rappresenta la vera operazione che deve essere eseguita.
In pratica quando quando viene richiamato StartTask il codice non fa altro che creare un nuovo Thread per eseguire il metodo Process().
Come si vede la classe SampleTask.cs espone una proprietà pubblica Step che identifica il punto dove è arrivato il lavoro, e se dovessero essere sollevate delle eccezioni non fa altro che riempire il field _exception che viene esposto tramite una proprietà dalla classe base.

Il fatto che l'operazione viene eseguita su un altro thread ecco che abbiamo risolto il primo problema....ovvero che una volta che l'utente lancia l'operazione puo continuare a lavorare su altre parti dell'applicazione.

Il secondo problema invece era quello di capire a che punto era arrivata l'operazione.
Nel progetto che troverete in allegato ci sono due pagine una Default.aspx ed una Pagina2.aspx.....la default è la pagina che realmente fa tutto mentre la pagina2 l'ho messa solo per simulare un'altra sezione utilizzata dall'utente.

Nella default.aspx ho messo questo codice

 <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <asp:UpdatePanel ID="upTask" runat="server">
            
<ContentTemplate>
                
<asp:Timer ID="Timer1" runat="server" Interval="1000" Enabled="False" OnTick="Timer1_Tick">
                
</asp:Timer>
                
<asp:Button ID="btnStart" runat="server" Text="Start" OnClick="btnStart_Click" />
                <
asp:Button ID="btnStop" Visible="false" runat="server" Text="Stop" OnClick="btnStop_Click" /><br />
                <
asp:Label ID="lblStep" runat="server"></asp:Label>
            
</ContentTemplate>
        
</asp:UpdatePanel>

All'interno dell'UpdatePanel sono presenti due bottoni Start e Stop una label per visualizzare i messagi di avanzamento del lavoro ed un ajax timer che ci permetterà ogni secondo di controllare lo stato del Task.

private SampleTask _task = null;
    protected void 
Page_Load(object sender, EventArgs e)
    {
        _task 
Cache["TASK"as SampleTask;
        if 
(_task != null)
        {
            
if (_task.Running)
            {
                TaskRunning()
;
            
}
            
else
            
{
                TaskNotRunning()
;
            
}
        }
    }

    
protected void TaskRunning()
    {
        Timer1.Enabled 
= true;
        
btnStart.Visible = false;
        
btnStop.Visible = true;
    
}

    
protected void TaskNotRunning()
    {
        Timer1.Enabled 
= false;
        
btnStart.Visible = true;
        
btnStop.Visible = false;
        
lblStep.Text = string.Format("Task inattivo");

        if 
(_task.Exception != null)
        {
            lblStep.Text 
_task.Exception.Message;
        
}

    }

    
protected void btnStart_Click(object sender, EventArgs e)
    {
        _task 
= new SampleTask();
        
Cache["TASK"_task;
        
_task.StartTask();
        
TaskRunning();
    
}

    
protected void btnStop_Click(object sender, EventArgs e)
    {
        _task.StopTask()
;
        
Cache.Remove("TASK");
        
TaskNotRunning();
        
lblStep.Text = string.Format("Task interrotto");    
    
}
    
    
protected void Timer1_Tick(object sender, EventArgs e)
    {
        lblStep.Text 
= string.Format("Operazioni concluse: {0}", _task.Step);
    
}
come si vede nel codebehind della pagina viene registrato in Cache la classe e viene controllata ad ogni Caricamento della pagina se la classe SampleTask è presente o meno nella cache comportandosi di conseguenza.
Il progetto di esempio lo potete scaricare da qui

Print | posted on giovedì 13 settembre 2007 13:10 | Filed Under [ ASP.NET AJAX Tips ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET