2013-10-30 10 views
13

Desidero scrivere un servizio che esegue il polling di un database ed esegue un'operazione in base ai dati da riportare.Creazione di un servizio di windows # per eseguire il polling di un database

Non sono sicuro di quale sia il modo migliore per farlo, posso trovare alcuni blog a riguardo e questa domanda di overflow dello stack Polling Service - C#. Tuttavia sono diffidente nel dire che sono tutti piuttosto vecchi e forse obsoleti.

Qualcuno può consigliarmi sull'attuale consiglio o le migliori pratiche (se ce ne sono) su come fare qualcosa del genere o indicarmi un post più recente su questo. Da ciò che posso raccogliere usando un timer o task TPL ci sono due modi potenziali per farlo.

Se timer sono ancora suggerito allora come faranno funzionano quando il servizio viene arrestato, perché le operazioni ho intenzione di questi servizi da fare potrebbero potenzialmente prendere oltre 30 minuti, questo è il motivo per cui dico Attività Utilizzare perché posso usare una cancellazione compito token ma questi generano eccezioni quando vengono cancellati (correggimi se sbaglio) e non penso di volere davvero questo comportamento (anche se correggimi se pensi che ci sia un motivo lo vorrò).

Mi dispiace che potrei chiedere parecchio in una sola domanda, ma non sono del tutto sicuro di ciò che sto chiedendo.

+0

Guarderei il modello produttore-consumatore per i principianti. Un thread può eseguire il polling e raccogliere informazioni sul lavoro, quindi passarlo al gestore della coda. –

+0

Con quale frequenza effettuerai il polling del database? È questo * tutto * che il servizio farà? Se tutto il servizio esegue il polling del database raramente e potenzialmente esegue un'attività molto lunga, probabilmente è meglio scrivere una semplice vecchia applicazione console e configurarla come operazione pianificata in Windows. –

+0

Hai qualche link su come questo può essere implementato nel codice Grant? Jim - Ho intenzione di sondarlo forse mai 2 minuti. E sì, questo è tutto ciò che il servizio farà, ma potrei aggiungere ulteriori funzionalità in seguito per monitorare anche un feed online. – user2647347

risposta

23

andare con un servizio di Windows per questo. L'utilizzo di un'attività pianificata non è una cattiva idea di per sé, ma dal momento che hai detto che i sondaggi possono verificarsi ogni 2 minuti, probabilmente stai meglio con il servizio. Il servizio ti consentirà di mantenere lo stato tra i sondaggi e avresti anche un maggiore controllo sui tempi dei sondaggi. Hai detto che l'operazione potrebbe richiedere più di 30 minuti dopo che è stata avviata, quindi forse vorrai rimandare i sondaggi fino al completamento dell'operazione. Questo è un po 'più facile da fare quando la logica viene eseguita come un servizio.

Alla fine non importa quale meccanismo si usi per generare i sondaggi. Potresti usare un timer o un thread/task dedicato che dorme o qualsiasi altra cosa. Personalmente, trovo il thread/task dedicato più facile da utilizzare rispetto a un timer per questo tipo di cose, perché è più facile controllare l'intervallo di polling. Inoltre, dovresti assolutamente usare il meccanismo di cancellazione cooperativa fornito con il TPL. Non è necessario lanciare eccezioni. Lo fa solo se chiami ThrowIfCancellationRequested. Puoi usare IsCancellationRequested invece per controllare lo stato del token di cancellazione.

Ecco un modello molto generico che è possibile utilizzare per iniziare.

public class YourService : ServiceBase 
{ 
    private CancellationTokenSource cts = new CancellationTokenSource(); 
    private Task mainTask = null; 

    protected override void OnStart(string[] args) 
    { 
    mainTask = new Task(Poll, cts.Token, TaskCreationOptions.LongRunning); 
    mainTask.Start(); 
    } 

    protected override void OnStop() 
    { 
    cts.Cancel(); 
    mainTask.Wait(); 
    } 

    private void Poll() 
    { 
    CancellationToken cancellation = cts.Token; 
    TimeSpan interval = TimeSpan.Zero; 
    while (!cancellation.WaitHandle.WaitOne(interval)) 
    { 
     try 
     { 
     // Put your code to poll here. 
     // Occasionally check the cancellation state. 
     if (cancellation.IsCancellationRequested) 
     { 
      break; 
     } 
     interval = WaitAfterSuccessInterval; 
     } 
     catch (Exception caught) 
     { 
     // Log the exception. 
     interval = WaitAfterErrorInterval; 
     } 
    } 
    } 
} 

Come ho detto, normalmente utilizzo un thread/task dedicato anziché un timer. Lo faccio perché il mio intervallo di polling non è quasi mai costante. Di solito inizio a rallentare i sondaggi se viene rilevato un errore transitorio (come problemi di disponibilità della rete o del server) in questo modo il mio file di registro non si riempie continuamente dello stesso messaggio di errore in rapida successione.

+0

Mi piace l'uso di WaitHandler, qui sembra che prenda un meccanismo più pulito, quindi un timer specifico. Dovrò usare questo in futuro. Grazie Brian! –

+0

Grazie, ho avuto qualcosa di simile ma la! Cancellazione.WaitHandle.WaitOne (intervallo) è ciò che penso mi mancasse. – user2647347

+0

Ho usato questo modello e talvolta il mio servie si limita a congelare e ho bisogno di terminare il processo per fermarlo, nessuna eccezione, nessuna idea? o cosa posso controllare? – kosnkov

3

Hai alcune opzioni. Per iniziare con quella che potrebbe essere essenzialmente l'opzione più semplice, è possibile decidere di creare l'app come applicazione console ed eseguire l'eseguibile come attività nell'Utilità di pianificazione di Windows. Tutto quello che devi fare è assegnare il tuo eseguibile come programma da avviare nell'attività e fare in modo che lo schedulatore funzioni l'intervallo di tempo per te. Questo è probabilmente il modo preferito se non ti interessa dello stato e ti impedirai di dovermi preoccupare di creare e gestire un servizio Windows se non ne hai davvero bisogno. Vedi il seguente link su come utilizzare lo scheduler.

Windows Task Scheduler

Il modo in cui accanto si possa fare questo sarebbe quello di creare un servizio di Windows e in quel servizio utilizzare un timer, in particolare System.Timers.Timer. In sostanza, si imposta l'intervallo del timer sulla quantità di tempo che si desidera passare prima di eseguire il processo. Quindi ti iscriverai all'evento tick del timer che si accenderà ogni volta che si è verificato quell'intervallo. In questo caso avresti essenzialmente il processo che vorresti eseguire; questo potrebbe dare il via ai thread aggiuntivi, se lo desideri. Quindi, dopo quella configurazione iniziale, chiameresti la funzione Start() del timer o imposterai la proprietà Enabled su True per avviare il timer. Un buon esempio di come sarebbe questo può essere trovato nell'esempio sulla pagina MSDN che descrive l'oggetto. Ci sono un sacco di tutorial là fuori che mostrano come impostare un servizio Windows, quindi non mi preoccuperò di entrare in quello specifico.

MSDN: System.Timers.Timer

Infine, e più complessa sarebbe quella di istituire un servizio di Windows che attende una SqlDependency. Questa tecnica è utile se le cose possono accadere nel database all'esterno dell'applicazione, ma è necessario renderle consapevoli nell'applicazione o in altri servizi. Il seguente link ha un buon tutorial su come impostare SqlDependency in un'applicazione.

Using SqlDependency To Monitor SQL Database Changes


Due cose che vorrei far notare dal tuo post originale che non sono specifici per la domanda che avevo.

  1. Se si sta scrivendo un servizio Windows vero, non si desidera interrompere il servizio. Il servizio dovrebbe essere costantemente in esecuzione e se si verifica un'eccezione dovrebbe essere gestito in modo appropriato e non interrompere il servizio.

  2. Un token di cancellazione non deve generare eccezioni; semplicemente non chiamando ThrowIfCancellationRequested() farà sì che l'eccezione non venga lanciata o se questa è una CancellazioneTokenSource imposta l'argomento su false sul metodo Cancel quindi successivamente controlla il token per vedere se la cancellazione è richiesta nei tuoi thread e ritorna fuori dal thread con garbo se è così.

Ad esempio:

CancellationTokenSource cts = new CancellationTokenSource(); 
    ParallelOptions options = new ParallelOptions 
    { 
     CancellationToken = cts.Token 
    }; 
    Parallel.ForEach(data, options, i => 
    { 
     try 
     { 
      if (cts.IsCancellationRequested) return; 

      //do stuff 

     } 
     catch (Exception ex) 
     { 
      cts.Cancel(false); 
     } 
    });