2009-08-04 12 views
16

Sto chiamando una procedura memorizzata dalla mia applicazione che può richiedere 30 minuti per essere eseguita.Eseguire una stored procedure da un modulo di Windows in modo asincrono e quindi disconnettersi?

Non voglio che il mio utente lasci l'applicazione aperta per l'intero periodo di tempo. Quindi mi piacerebbe chiamare lo sproc, lasciarlo volare e lasciarli chiudere l'applicazione e tornare più tardi.

Come posso fare questo?

+0

Bene, non accedo o effettuo la registrazione (un po 'deluso dal fatto che non posso votare o chiuderlo senza farlo). Ad ogni modo, Scott W. ha dato la risposta migliore e poi è stato sostenuto da alcuni post di supporto. Tutti accettano il bit "... buy hardware più veloce". Prendevo appena in considerazione l'insuccesso, e poi dico al mio cliente che hanno bisogno di acquistare hardware più veloce, perché non riesco a farlo correttamente. Grazie per tutto il vostro aiuto! –

+2

@Dan - Sono confuso. Hai già effettuato l'accesso, almeno quanto basta per avere un account utente e 2 badge. Perché non sei in grado di fare clic sul pulsante Accetta risposta? –

+0

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

risposta

1

farli arrestare l'applicazione e vengono tornare più tardi

Se avete intenzione di consentire loro di completamente chiudere l'applicazione, dovrete avviare un .exe separato o qualcosa in un ThreadPool diverso che esegue il codice chiamando la stored procedure. Altrimenti il ​​tuo thread morirà quando chiudi l'app.

+0

Sono d'accordo, se vuoi la tua applicazione per essere in grado di chiudere dovrai spostare completamente la chiamata sproc fuori dal processo originale. – Joshua

3

È possibile utilizzare i metodi BeginExecuteXXX/EndExecuteXXX (a seconda se restituisca un risultato o meno) di SqlCommand, passando un delegato di richiamata.

+0

Non so perché la gente scende a votare senza spiegare perché. Questa è una risposta perfettamente valida se volevano eseguire la procedura senza spegnere completamente il programma. – Joshua

+0

Posso indovinare il motivo: il thread ThreadPool utilizzato per BeginXXX verrà interrotto quando il thread principale esiste. Tuttavia, ho presupposto che l'OP volesse mantenere l'app aperta in background piuttosto che chiudere completamente il processo (che credo sia un'ipotesi valida). –

+0

Non intendevo votare giù se l'avessi fatto (nuovo per questo forum, quindi potrei aver cliccato su un incidente e non ho capito cosa stavo facendo). Sono d'accordo, soluzione perfettamente buona ... ma ahimè, voglio lasciarli completamente uccidere l'app. Thx Mehrdad. –

2

Suggerisco una re-architettura. Creare una tabella "coda di lavoro" in cui si registrano richieste per eseguire la stored procedure. Quindi, avere un servizio di Windows o un lavoro di SQL Server controllare che di tanto in tanto lavori (o essere davvero geniale e utilizzare un trigger) per avviare la stored procedure. La procedura memorizzata aggiorna i progressi di volta in volta nella tabella della coda di lavoro e il front-end può controllare che l'utente indichi l'avanzamento e quindi visualizzare i risultati al termine.

+0

Bene, non ho intenzione di accedere o registrarmi (un po 'deluso dal fatto che non posso votare o chiudere questo senza farlo). Ad ogni modo, questa è stata la migliore risposta per me e poi è stata sostenuta da alcuni post di supporto. Tutti accettano il bit "... buy hardware più veloce". Prendevo appena in considerazione l'insuccesso, e poi dico al mio cliente che hanno bisogno di acquistare hardware più veloce, perché non riesco a farlo correttamente. Grazie per tutto il vostro aiuto! –

+0

@Dan - il "comprare hardware più veloce" stava scherzando. Lo modificherò per rimuoverlo. Ma l'hardware più veloce a volte è un'opzione. Non troverai hardware che sia 30 volte più veloce! ;) –

+1

@Scot, si sta essenzialmente descrivendo una funzione integrata: Attivazione di Service Broker.Sia l'attivazione interna che quella esterna sono probabilmente migliori di un servizio Windows o di un controllo dei lavori dell'agente di volta in volta. –

1

Un altro metodo che si potrebbe fare sarebbe quello di consentire all'applicazione di funzionare in background (possibilmente nell'area di notifica) e quindi uscire o notificare quando il lavoro è completato. È possibile utilizzare ciò utilizzando i metodi BeginExecuteNonQuery e EndExecuteNonQuery per consentirne l'esecuzione in un thread separato.

2

Se si desidera chiudere completamente l'applicazione, suggerisco di definire un lavoro in SQL Server Agent ed è sufficiente eseguire un'istruzione T-SQL per avviare manualmente quel lavoro. La sintassi è:

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] 

Il lavoro dovrebbe eseguire la procedura memorizzata. Dovrai essere un po 'creativo per passare qualsiasi argomento. Ad esempio, inserire i parametri in una tabella "coda" e fare in modo che il processo elabori tutte le righe nella coda.

Invece di un lavoro, dovrebbe funzionare anche un trigger di inserimento sulla coda.

0

Non è necessario che la finestra principale dell'applicazione sia aperta. Se lo hai lanciato come thread secondario, continuerà a funzionare fino a IsBackground == false. Di solito preferisco fare queste cose tramite SQL Server Agent o come applicazione client-server (nulla impedisce che un'applicazione client-server sia in esecuzione sullo stesso computer, sia che sia lo stesso binario).

E 'stato un po' ...

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

Io preferisco usare un servizio in background per l'elaborazione in linea, in cui la vostra applicazione utente comunicato al servizio di cosa fare e poi si disconnette. Il servizio può registrare i tempi trascorsi e gli errori/lo stato e riavviare se necessario. WCF è progettato per questo e supporta le code con cui comunicare.

+2

Remus ha avuto una buona risposta e apprezzo che si sia preso il tempo di postare l'articolo del blog. Sarebbe molto meno lavoro rispetto alla creazione di un servizio di background separato se applicabile al tuo problema. –

24

Questo è in realtà uno scenario abbastanza comune.Non puoi fare nulla in base al cliente perché il cliente potrebbe andarsene e disconnetterti e perderai il lavoro raggiunto finora. La soluzione è utilizzare Service Broker Activation: si crea un servizio nel database e si allega una procedura attivata. Nell'applicazione (o nella pagina ASP) si invia un messaggio al servizio e si incorporano i parametri necessari per la propria procedura. Dopo il commit dell'applicazione, il messaggio attiva la procedura di servizio. la procedura di servizio legge i parametri dal messaggio e richiama la procedura. poiché l'attivazione avviene su un thread del server non correlato alla tua connessione originale, questo è affidabile. Infatti, il server può anche arrestare e riavviare mentre la procedura è in esecuzione e il lavoro verrà ripristinato, quindi riprendere, poiché il messaggio di attivazione attiverà nuovamente la procedura di servizio dopo il riavvio.

Aggiornamento

Ho pubblicato i dettagli di come fare questo codice di esempio anche per quanto riguarda il mio blog: Asynchronous procedure execution.

+2

Ottimi dettagli sul blog! –

+2

Sì! Grazie mille. Una cosa è certa: il broker di servizi deve essere abilitato per il database. (Proprietà database -> Opzioni in SSMS) o ALTER DATABASE ... SET ENABLE_BROKER. La cosa bella è che funziona anche in Express Edition di SQL Server! – tony722

+0

@Rusus Rusanu, ho provato il codice del tuo articolo (sembra che i commenti siano disabilitati lì) e si sia imbattuto in un comportamento strano. Se scrivo un processo memorizzato su INSERT INTO in una tabella nel mio db e poi in un WAITFOR, quindi lo sto eseguendo tramite ups_AsyncExecInvoke, ottengo la mia intera tabella bloccata mentre viene eseguito proc (ad esempio non posso più fare SELECT su quella tabella); se eseguo lo stesso proc direttamente (senza il messaggio Broker) la tabella non blocca. Qualche idea? – zaitsman

Problemi correlati