2009-08-04 7 views
16

J'appelle une procédure stockée de mon application qui peut prendre 30 minutes pour s'exécuter.Exécuter une procédure stockée à partir d'un formulaire Windows de manière asynchrone, puis déconnecter?

Je ne souhaite pas que mon utilisateur laisse l'application ouverte pendant toute cette période. Donc, je voudrais appeler le sproc, le laisser voler, et les laisser fermer l'application et revenir plus tard.

Comment est-ce que je peux faire ceci?

+0

Eh bien, je ne vais pas vous connecter ou vous inscrire (sorte déçu que je ne peux pas voter ou fermer cette sans le faire). Quoi qu'il en soit, Scott W. a donné la meilleure réponse, puis a été soutenu par quelques messages de soutien. Tous acceptent le bit "... acheter du matériel plus rapide". Je supposerais simplement l'échec de la réclamation, plutôt que de dire à mon client qu'il doit acheter du matériel plus rapide, parce que je ne peux pas le faire correctement. Merci pour votre aide! –

+2

@Dan - Je suis confus. Vous êtes déjà connecté, au moins assez pour avoir un compte utilisateur et 2 badges. Pourquoi ne pouvez-vous pas cliquer sur le bouton accepter la réponse? –

+0

Voir aussi ... http://stackoverflow.com/questions/7444215/how-to-execute-a-sql-server-stored-procedure-asynchronously-and-ensure-its-comp – SteveC

Répondre

1

laisser les arrêter l'application et viennent revenir plus tard

Si vous allez leur permettre de complètement fermer l'application, vous devez commencer un .exe séparé ou quelque chose dans un ThreadPool différent qui exécute votre code appelant la procédure stockée. Sinon, votre thread va mourir lorsque vous fermez l'application.

+0

Je suis d'accord, si vous voulez Pour que votre application puisse se fermer, vous devez complètement retirer l'appel du sproc du processus d'origine. – Joshua

3

Vous pouvez utiliser les méthodes BeginExecuteXXX/EndExecuteXXX (en fonction du résultat renvoyé ou non) du SqlCommand, en passant un délégué de rappel.

+0

Je ne sais pas pourquoi les gens votent sans expliquer pourquoi. C'est une réponse parfaitement valide s'ils veulent exécuter la procédure sans arrêter complètement le programme. – Joshua

+0

Je peux deviner la raison: le thread ThreadPool utilisé pour BeginXXX sera annulé lorsque le thread principal existe. Cependant, je supposais l'OP veut maintenir l'application ouverte en arrière-plan plutôt que arrêter complètement le processus (qui je pense est une hypothèse valable.) –

+0

Je ne voulais pas vers le bas vote si je l'ai fait (nouveau à cette forum, donc j'ai peut-être cliqué sur accident et n'ai pas réalisé ce que je faisais). Je suis d'accord, parfaitement bonne solution ... mais hélas, je veux les laisser complètement tuer l'application. Thx Mehrdad. –

2

Je suggère une réarchitecture. Créez une table "file d'attente de travail" dans laquelle vous consignez les demandes pour exécuter la procédure stockée. Ensuite, ayez un service Windows ou un travail SQL Server vérifiez cette file d'attente de temps en temps (ou soyez vraiment ingénieux et utilisez un déclencheur) pour lancer la procédure stockée. Avoir la procédure stockée mettre à jour la progression de temps en temps dans la table de file d'attente de travail, et votre front-end peut regarder que dire à l'utilisateur la progression, puis afficher les résultats quand ils sont faits.

+0

Eh bien, je ne vais pas me connecter ou m'enregistrer (en quelque sorte déçu que je ne peux pas voter ou fermer ceci sans le faire). Quoi qu'il en soit, c'était la meilleure réponse pour moi, puis a été soutenu par quelques messages de soutien. Tous acceptent le bit "... acheter du matériel plus rapide". Je supposerais simplement l'échec de la réclamation, plutôt que de dire à mon client qu'il doit acheter du matériel plus rapide, parce que je ne peux pas le faire correctement. Merci pour votre aide! –

+0

@Dan - le "acheter du matériel plus rapide" plaisantait. Je vais l'éditer pour l'enlever. Mais le matériel plus rapide est parfois une option. Vous n'allez pas trouver du matériel 30 fois plus rapide! ;) –

+1

@Scot, vous décrivez essentiellement une fonctionnalité intégrée: Service Broker Activation.Les activations Interne et Externe sont sans doute meilleures qu'un service Windows ou un job Agent de temps en temps. –

1

Une autre méthode que vous pourriez faire serait de permettre à votre application de s'exécuter en arrière-plan (éventuellement dans la zone de notification), puis de quitter ou notifier quand le travail se termine. Vous pouvez l'utiliser en utilisant les méthodes BeginExecuteNonQuery et EndExecuteNonQuery pour lui permettre de s'exécuter dans un thread distinct.

2

Si vous voulez vraiment fermer complètement votre application, je vous suggère de définir un travail dans SQL Server Agent, et d'exécuter simplement une instruction T-SQL pour démarrer ce travail manuellement. La syntaxe est la suivante:

sp_start_job 
    { [@job_name =] 'job_name' 
     | [@job_id =] job_id } 
    [ , [@error_flag =] error_flag] 
    [ , [@server_name =] 'server_name'] 
    [ , [@step_name =] 'step_name'] 
    [ , [@output_flag =] output_flag] 

Le travail exécutera votre procédure stockée. Vous devrez être un peu créatif pour passer des arguments. Par exemple, insérez les paramètres dans une table "file d'attente" et demandez au travail de traiter toutes les lignes de la file d'attente.

Au lieu d'un travail, un déclencheur d'insertion dans votre file d'attente devrait également fonctionner.

0

La fenêtre principale de votre application n'a pas besoin d'être ouverte. Si vous l'avez lancé en tant que thread secondaire, il continuera à fonctionner aussi longtemps que IsBackground == false. Je préfère généralement faire ce travail via SQL Server Agent ou en tant qu'application client-serveur (rien n'empêche une application client-serveur de s'exécuter sur la même machine, ou même d'être le même binaire).

Il a été un moment ...

using System.Threading; 

..... 

Thread _t = null; 
void StartProcedure() 
{ 
    _t = new Thread(new ThreadStart(this.StartProc)); 
    _t.IsBackground = false;//If I remember correctly, this is the default value. 
    _t.Start(); 
} 

bool ProcedureIsRunning 
{ 
get { return _t.IsRunning; } //Maybe it's IsActive. Can't remember. 
} 

void StartProc(object param) 
{ 
    //your logic here.. could also do this as an anonymous method. Broke it out to keep it simple. 
} 
2

Je préfère utiliser un service de base pour le traitement hors ligne, où votre application utilisateur indique le service ce qu'il faut faire et puis se déconnecte. Le service peut enregistrer les temps écoulés et les erreurs/états, et redémarrer si nécessaire. WCF est conçu pour cela et prend en charge les files d'attente pour communiquer avec.

+2

Remus a eu une bonne réponse, et je l'apprécie de prendre le temps de poster l'article de blog. Ce serait beaucoup moins de travail que de créer un service d'arrière-plan séparé si c'est applicable à votre problème. –

24

Ceci est en fait un scénario assez commun.Vous ne pouvez rien faire en fonction du client parce que le client peut s'en aller et se déconnecter et vous perdrez le travail accompli jusqu'à présent. La solution consiste à utiliser Service Broker Activation: vous créez un service dans la base de données et attacher une procédure activée. Dans votre application (ou page ASP), vous envoyez un message au service et intégrez les paramètres nécessaires à votre procédure. Une fois votre application validée, le message active la procédure de service. la procédure de service lit les paramètres du message et appelle votre procédure. puisque l'activation se produit sur un thread de serveur sans rapport avec votre connexion d'origine, ceci est fiable. En fait, le serveur peut même arrêter et redémarrer tout votre procédure est en cours d'exécution et le travail sera annulée puis a repris, depuis le message d'activation déclenche à nouveau la procédure de service après le redémarrage.

Mise à jour

J'ai publié les détails sur la façon de le faire, y compris exemple de code sur mon blog: Asynchronous procedure execution.

+2

Excellents détails sur le blog! –

+2

Oui! Merci beaucoup. Une chose à vérifier: Le courtier de service doit être activé pour la base de données. (Propriétés de la base de données -> Options dans SSMS) ou ALTER DATABASE ... SET ENABLE_BROKER. La bonne chose est que cela fonctionne même dans les éditions Express de SQL Server! – tony722

+0

@Remus Rusanu, j'ai essayé le code de votre article (semble que les commentaires sont désactivés là) et est tombé sur un comportement étrange. Si j'écris un proc stocké à INSERT INTO dans une table dans mon DB puis WAITFOR puis l'exécute par le ups_AsyncExecInvoke j'obtiens toute ma table bloquée pendant que le proc s'exécute (par exemple je ne peux plus faire SELECT sur cette table); si je cours directement le même proc (sans le message de courtier) la table ne bloque pas. Des idées? – zaitsman

Questions connexes