2011-05-18 6 views
2

J'ai une page qui génère un tas de rapports, et envoie un e-mail des résultats. Ce processus prend environ 30sec à 2min. Lorsque l'utilisateur invoque ce processus via ajax, je peux interroger le service Web via ajax pour obtenir les résultats, mais comment créer une méthode asynchrone qui mettra à jour une variable en continu? Pour simplifier, comment pourrais-je écrire une méthode qui compterait de 1 à 1 million et ajouter chaque nombre à une liste, et en même temps être en mesure d'interroger le service Web et retourner le nombre actuel d'éléments dans ledit liste?Méthode asynchrone dans le service Web asp.net

Des articles seraient également appréciés.

+0

Etes-vous en train d'implémenter un indicateur de progression sur le client pendant que le serveur est en train de croquer le rapport? Je veux m'assurer de comprendre la question avant de tenter de décrire la solution :). –

+0

Oui, essentiellement mon bouton "Générer des rapports" utiliserait AJAX pour appeler une méthode Web pour démarrer le processus, alors je voudrais interroger le serveur toutes les secondes ou deux pour voir ce que la progression est. Si je compte jusqu'à 10 millions et que je stocke chaque résultat dans une variable, ma fonction d'interrogation AJAX devrait être capable de récupérer la valeur actuelle. –

Répondre

1

D'abord, quelques réflexions de fond:

1) Comme il est un travail de longue haleine en cours d'exécution, vous pouvez un cache d'emplois précédemment exécutés de sorte que vous ne dupliquez pas le travail et surchargeant votre serveur.

2) Vous ne devriez pas avoir à démarrer un thread d'arrière-plan séparé, puisque chaque requête ASP.NET va être en cours d'exécution dans son propre thread. Toutefois, si vous trouvez que vous êtes affamé votre processus de travail ASP.NET, vous pouvez rendre vos méthodes Web asynchrones côté serveur - voir: this article.

L'implémentation nécessite trois composants: un cache, une méthode Web pour exécuter le travail et une méthode Web pour vérifier l'état du travail. Vous devrez également être en mesure de générer une clé permettant au système de suivre les demandes. Vous pouvez générer ce côté serveur (lorsque vous générez la page), ou côté client (si vous avez un algorithme intelligent qui peut générer des identifiants uniques).

Le cache peut être défini sur une application si vous souhaitez que les utilisateurs puissent partager des travaux en cours d'exécution ou que la portée soit limitée. Le cache est simplement un dictionnaire contenant des références à vos instances de travail en cours d'exécution.

Un exemple simple (ceci ne compilera pas, contient probablement des erreurs de syntaxe, et est principalement destiné à faire passer le point).

serveur pseudo-code:

//In application Session_Start, 
Session["ReportCache"] = new Dictionary<string, ReportStatus>(); 

public class ReportResults 
{ 
} 

public class ReportStatus 
{ 
    public int PercentComplete = 0; 
} 

[WebMethod(EnableSessions = true)] 
ReportResults RunReport(string uniqueid) 
{ 
    ReportStatus status = new ReportStatus(); 
    Session["ReportStatus"].Add(uniqueid, status); 
    //Start report 
    for(int i = 0; i < 100000; ++i) 
    { 

     //update the report status 
     status.PercentComplete = 100 * 100000 * i/100000; 
    } 
    Session["ReportStatus"].Remove(uniqueid); 

} 

[WebMethod(EnableSessions=true)] 
public ReportStatus GetStatus(uniqueid) 
{ 
    try 
    { 
     return Session["ReportStatus"][uniqueid]; 
    } 
    catch(Exception) 
    { 
     return null; 
    } 
} 

Sur le client, utilisez ajax pour appeler la première méthode Web. Il n'appelle pas le rappel onSuccess tant que le rapport n'est pas terminé.Utilisez setTimeout pour interroger périodiquement la seconde méthode Web pour obtenir des informations d'état et mettre à jour le client. Lorsque la première méthode Web est terminée, annulez votre minuterie.

Votre plus gros problème sera le fait que votre serveur exécutera de nombreux travaux de longue durée, ce qui peut dégrader les performances globales et la réactivité. Vous pourriez vouloir adopter une approche complètement différente. Par exemple, plutôt que d'exécuter les rapports à la demande par utilisateur, vous pouvez mettre en file d'attente la liste des personnes souhaitant recevoir le rapport. Une fois par heure, par jour et par semaine, vous pouvez exécuter un rapport distinct. et l'envoyer.

0

Vous devez exécuter votre processus de longue durée sur un thread distinct, de sorte que votre application Web ne semble pas aller au restaurant quand elle lance ces rapports. Mais vous voulez également signaler l'état de votre chemin dans ce processus (via l'interrogation AJAX). Je suggère que votre service Web utilise un objet qui gère cela pour vous. Peut-être quelque chose comme ceci:

public class ReportGenerator { 
    public string CurrentStatus; 

    public void Run() { 
    var worker = new Thread(new ThreadStart(this.ExportReports)); 
    worker.Start(); 
    } 

    private void ExportReports() { 
    this.CurrentStatus = "started"; 
    /* export a report */ 
    this.CurrentStatus = "1 report complete"; 
    /* export another report */ 
    this.CurrentStatus = "2 reports complete"; 
    /* etc, etc, etc */ 
    } 
} 

Votre service web devra être capable de créer et de gérer une instance de cet objet. Allez-vous autoriser uniquement l'une de ces exportations à la fois dans l'ensemble de l'application? Si c'est le cas, utilisez l'Singleton Pattern pour cet objet et assurez-vous qu'il n'est pas déjà en cours d'exécution avant de le redémarrer.

Si plusieurs utilisateurs peuvent exécuter ce processus simultanément, vous devrez instancier cet objet et stocker une référence dans la session de l'utilisateur ou quelque chose comme ça, ainsi quand vous interrogez le service Web, il peut récupérer l'objet et vérifiez son état, puis renvoyez cette information au client. Vous pouvez rencontrer des situations vraiment collantes avec ce type de threads, alors soyez très prudent.

+0

ce sera pour un seul utilisateur (mais il est toujours bon de savoir comment le faire fonctionner pour plusieurs utilisateurs comme vous l'avez mentionné). Je vais essayer ça un peu. –

Questions connexes