web-dev-qa-db-ja.com

データベースをポーリングするためのc#Windowsサービスの作成

データベースをポーリングし、返されるデータに応じて操作を実行するサービスを作成したいと考えています。

これを行うための最良の方法がわかりません。それに関するいくつかのブログと、このスタックオーバーフローの質問を見つけることができます ポーリングサービス-C# 。しかし、私はそれらがすべてかなり古く、おそらく時代遅れであることを警戒しています。

誰かがこのようなことをする上での現在のアドバイスやベストプラクティス(もしあれば)について私にアドバイスしたり、これについてのより最近のブログ投稿の方向に私を向けたりできますか?タイマーまたはtplタスクを使用して収集できるものから、これを行う2つの潜在的な方法があります。

それでもタイマーが提案されている場合、これらのサービスで実行する予定の操作には30分以上かかる可能性があるため、サービスが停止したときにタイマーはどのように機能しますか?これが、タスクキャンセルトークンを使用できるため、タスクを使用すると言う理由ですが、これらはスローされますキャンセルされたときの例外(私が間違っている場合は私を訂正してください)そして私は本当にその振る舞いを望んでいないと思います(私がそれを望む理由があると思うなら私を訂正してください)。

1つの質問でかなり多くの質問をしている可能性がありますが、何を質問しているのか完全にはわかりません。

18
user2647347

これにはWindowsサービスを使用してください。スケジュールされたタスクを使用すること自体は悪い考えではありませんが、ポーリングは2分ごとに発生する可能性があると述べたので、おそらくサービスを利用したほうがよいでしょう。このサービスを使用すると、ポーリング間の状態を維持でき、ポーリングのタイミングもより細かく制御できます。操作が開始されてから30分以上かかる可能性があるため、操作が完了するまでポーリングを延期することをお勧めします。ロジックをサービスとして実行すると、これは少し簡単になります。

結局、ポーリングを生成するためにどのメカニズムを使用するかは実際には重要ではありません。タイマーや、スリープ状態になる専用のスレッド/タスクなどを使用できます。個人的には、ポーリング間隔の制御が簡単なため、この種のタイマーよりも専用のスレッド/タスクの方が扱いやすいと思います。また、TPLで提供されている協調キャンセルメカニズムを必ず使用する必要があります。例外をスローする必要はありません。 ThrowIfCancellationRequestedを呼び出した場合にのみそうします。代わりにIsCancellationRequestedを使用して、キャンセルトークンの状態を確認することができます。

これは、始めるために使用できる非常に一般的なテンプレートです。

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;
      }
    }
  }
}

私が言ったように、私は通常、タイマーの代わりに専用のスレッド/タスクを使用します。これを行うのは、ポーリング間隔がほぼ一定ではないためです。私は通常、一時的なエラー(ネットワークやサーバーの可用性の問題など)が検出された場合にポーリングの速度を落とし始めます。そうすれば、ログファイルに同じエラーメッセージが連続して何度も表示されなくなります。

29
Brian Gideon

いくつかのオプションがあります。本質的に最も簡単なオプションから始めるために、アプリをコンソールアプリケーションとして作成し、実行可能ファイルをWindowsタスクスケジューラでタスクとして実行することを決定できます。あなたがする必要があるのは、タスクで開始するプログラムとして実行可能ファイルを割り当て、タスクスケジューラにタイミング間隔を処理させることだけです。これは、状態を気にしない場合におそらく推奨される方法であり、本当に必要がない場合にWindowsサービスの作成と管理について心配する必要がなくなります。スケジューラの使用方法については、次のリンクを参照してください。

Windowsタスクスケジューラ

これを行う次の方法は、Windowsサービスを作成し、そのサービスでタイマー、具体的にはSystem.Timers.Timerを使用することです。基本的に、タイマー間隔は、プロセスを実行する前に経過させたい時間に設定します。次に、その間隔が発生するたびに発生するタイマーティックイベントにサインアップします。この場合、基本的に実行したいプロセスがあります。必要に応じて、これで追加スレッドを開始できます。次に、その初期設定の後、タイマーのStart()関数を呼び出すか、EnabledプロパティをTrueに設定してタイマーを開始します。これがどのように見えるかの良い例は、オブジェクトを説明しているMSDNページの例にあります。 Windowsサービスを設定する方法を示すチュートリアルはたくさんあるので、特に詳しくは説明しません。

MSDN:System.Timers.Timer

最後に、さらに複雑なのは、SqlDependencyをリッスンするWindowsサービスをセットアップすることです。この手法は、アプリケーションの外部のデータベースで問題が発生する可能性があるが、アプリケーションまたはその他のサービスでそれを認識する必要がある場合に役立ちます。次のリンクには、アプリケーションでSqlDependencyを設定する方法に関する優れたチュートリアルがあります。

SQLデータベースの変更を監視するためのSqlDependencyの使用


あなたの元の投稿から、あなたが持っていた質問に固有ではない2つのことを指摘したいと思います。

  1. 真のWindowsサービスを作成している場合は、サービスを停止したくありません。サービスは常に実行されている必要があり、例外が発生した場合は、サービスを停止せずに適切に処理する必要があります。

  2. キャンセルトークンは例外をスローする必要はありません。 ThrowIfCancellationRequested()を呼び出さないと、例外がスローされないか、これがCancellationTokenSourceの場合は、Cancelメソッドで引数をfalseに設定し、その後トークンをチェックして、スレッドでキャンセルが要求されているかどうかを確認し、スレッドから正常に戻ります。もしそうなら。

例えば:

    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);
        }
    });  


3
Logan G.