Publier Profil Docs FAQ

Solution

Lancer une tache de fond avec AJAX et les multithreads


Le but de ce tutoriel est de permettre de lancer des taches longues sans bloquer l'application, et permettre une visualisation en temps réel de l'avancement de la tache.

Pour effectuer cela, nous utiliserons les threads (System.threading) et les extensions AJAX de Microsoft.

Avant de débuter certaines contditions sont requises. L'action longue doit être inégrée a une classe, on doit pouvoir la lancer de cette manière

MonObjet.tacheLongue(); 

Il faudra pour suivre l'avancement de la tache que la classe possède un attribut public contenant un message qui donne l'état de la tache.

private String messageEtat; 
Attention ! La méthode qui sera appelée dans  un nouveau thread ne doit pas faire appel a des variables de sessions directement, donc pas de déclaration du type String machin = Session["machin"]; Ceci est aussi valable pour l'accès aux variables de type Server. D'une manière générale considérez votre classe comme indépendante a votre application et injectez les données nécéssaire a partir de la page aspx avant le lancement du thread.

Une fois les conditions réunies on peut passer a la préparation de la page aspx et de la partie qui va se charger de l'affichage de l'avancement de la tâche.
Après avoir placé le ScriptManager obligatoire pour toute page AJAX nous alons insérer un updatePanel a l'endroit désiré.

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <
ContentTemplate>
        <asp:Label ID="messageLabel" runat="server">asp:Label><br />
        <asp:HyperLink ID="actionLink" runat="server" Target="_blank" Visible="False"
         Text="Telecharger le fichier généré">asp:HyperLink><br />
        <asp:Timer ID="Timer1" runat="server" Enabled="false" Interval="25" OnTick="Timer1_Tick">asp:Timer>
   
<ContentTemplate>
<
asp:UpdatePanel>

L'update panel contient un Label pour le message de statut et un HyperLink placé ici pour montrer qu'il est possible d'ajouter d'autres controles qui seront modifiés selon l'état de la tache.  Notez aussi la présence du timer qui s'occupera de rafraichir le panel a intervalle réguliers. 

Occupons nous maintenant de la fonction Timer1_Tick

/// <summary>

/// Timer sur le update panel AJAX / Multithread

/// Permet d'afficher le statut de la tache lancée en fond

/// </summary>

protected void Timer1_Tick (object sender, EventArgs e)

{

       classeLongue longTask = (classeLongue)Session ["longTask"];

       if (longTask!= null) {

              messageLabel.Text = longTask.Message;

              if (longTask.Message.Contains ("ok")) {  //L'import est réussi

                     messageLabel.Visible = false;

                     actionLink.Visible = true;

                     Session ["longTask"] = null;

                     Timer1.Enabled = false;

              }

       }

}

Pour ne pas perdre la tache de fond entre les postback et autres changements de page, il faudra la conserver dans une variable Session. Je ne sais pas ceci reflète la meilleure pratique mais c'est la méthode que j'utilise dans mes applications ASP.NET et elle fonctionne bien. La première étape consiste donc a récupérer l'objet que nous avons stocké dans une variable de session.
S'il existe, nous affichons le message longTask.Message sur le label prévu a cet effet. La fonction pourait s'arréter ici, mais nous allons prendre en charge la fin de la tache, signalé dans l'exemple par le message "ok". Celui ci provoque le l'affichage de l'HyperLink et le masquage du Label de statut.L'objet contenant la tache a executer peut être supprimé et le timer arrété.

Il ne reste plus maintenant qu'a écrire l'évènement qui va lancer la tache :

 

protected void launchTask_Click (object sender, EventArgs e)

{

classeLongue longTask = new classeLongue ();

       longTask.IdUser = Convert.ToInt32 (HttpContext.Current.Session ["IdUser"]);

       longTask.SavePath = HttpContext.Current.Server.MapPath ("saveDirectory");

       Thread taskThread = new Thread (new ThreadStart (longTask.runLongTask));

       taskThread.Start ();

       Session ["longTask"] = (classeLongue)longTask;

       Timer1.Enabled = true;

       messageLabel.Visible = true;

}

Dans l'exemple ci dessus, on montre bien que les variables de type Session et Server sont envoyées a l'objet avant de lancer la tache.
L'objet longTask est stocké en session pour pouvoir le récupérer par la suite et le timer est démarré.

Voila nous avons lancé notre tache dans un nouveau thread. Au final, le plus dur doit être de bien isoler la tache de fond dans une classe indépendante.