C#で記述されたサービスをデバッグしたいのですが、昔ながらの方法では長すぎます。サービスを停止し、デバッグモードでサービスを使用するアプリケーション(Visual Studio 2008)を起動し、サービスを開始し、サービスプロセスに接続してから、Asp.Netアプリケーションに移動してサービスをトリガーする必要があります。
基本的に、サービスをバックグラウンドで実行し、タスクを待機しています。 Webアプリケーションは、サービスによって取得されるタスクをトリガーします。
私がやりたいのは、デバッグのためにサービスを起動するコンソールアプリケーションを用意することです。誰かが知っている簡単なデモはありますか?
メインのエントリポイントで次のようなことができます。
static void Main()
{
#if DEBUG
Service1 s = new Service1();
s.Init(); // Init() is pretty much any code you would have in OnStart().
#else
ServiceBase[] ServicesToRun;
ServicesToRun=new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
#endif
}
およびOnStartイベントハンドラー内:
protected override void OnStart(string[] args)
{
Init();
}
私が常に採用しているアプローチは、アプリケーションのすべてのロジックをクラスライブラリに分離することです。これにより、サービスプロジェクトは、クラスライブラリをサービスとしてホストする単なるシェルになります。
これにより、プロセスにアタッチしてサービスをデバッグするという頭痛の種に対処することなく、コードの単体テストとデバッグを簡単に行うことができます。もちろん、単体テストをお勧めしますが、それを行わない場合は、サービスと同じエントリポイントを呼び出すコンソールアプリケーションを追加することをお勧めします。
グローバル定義の使用を避けるために、私は通常、Environment.UserInteractiveプロパティを介して、サービスであるか通常のアプリケーションであるかを実行時にテストします。
[MTAThread()]
private static void Main()
{
if (!Environment.UserInteractive)
{
ServiceBase[] aServicesToRun;
// More than one NT Service may run within the same process. To add
// another service to this process, change the following line to
// create a second service object. For example,
//
// ServicesToRun = New System.ServiceProcess.ServiceBase () {New ServiceMain, New MySecondUserService}
//
aServicesToRun = new ServiceBase[] {new ServiceMain()};
Run(aServicesToRun);
}
else
{
var oService = new ServiceMain();
oService.OnStart(null);
}
}
あなたはあなたの冒険でもTopShelfをチェックしたいかもしれません。
http://codebetter.com/blogs/dru.sellers/archive/2009/01/11/topshelf.aspx
以下に示すように、リフレクションを介してサービスメソッドを呼び出すことができます。
Environment.UserInteractive
を使用すると、コンソールアプリとして実行しているか、サービスとして実行しているかを知ることができます。
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
if (!Environment.UserInteractive)
{
// This is what normally happens when the service is run.
ServiceBase.Run(ServicesToRun);
}
else
{
// Here we call the services OnStart via reflection.
Type type = typeof(ServiceBase);
BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo method = type.GetMethod("OnStart", flags);
foreach (ServiceBase service in ServicesToRun)
{
Console.WriteLine("Running " + service.ServiceName + ".OnStart()");
// Your Main method might not have (string[] args) but you could add that to be able to send arguments in.
method.Invoke(service, new object[] { args });
}
Console.WriteLine("Finished running all OnStart Methods.");
foreach (ServiceBase service in ServicesToRun)
{
Console.WriteLine("Running " + service.ServiceName + ".OnStop()");
service.Stop();
}
}
TopShelf は、このアプローチに最適な別のプロジェクトです。これにより、プロセスをサービスとして、または最小限の構成で通常のコンソールアプリケーションとして実行できます。
私は設定設定を持っているか、デバッグビルドにディレクティブを使用する傾向があります:
#if DEBUG
Debugger.Break();
#endif
または
if(Settings.DebugBreak)
Debugger.Break();
それをサービスコンポーネントのOnStartメソッドに入れました。次に、自動的にプロンプトが表示され、プロセスに接続されます。
これが ブログ投稿 Windowsサービスをコンソールアプリとして実行することについてです。
また、サービスと同じロジックを参照してメソッドをテストする新しいコンソールアプリケーションを作成したり、サービスのロジックに単体テストを設定したりすることもできます。
私は過去にユニットテストを使用して難しいセットアップをデバッグしました。ユニットテストで任意のサービスメソッドを呼び出し、任意のパラメーターでデバッグブレークポイントを設定するユニットテストを作成するだけです。
Testdriven.netまたはjetbrainstestrunnerを使用すると、簡単になります。
これを使用して、プロセスがサービスとして実行されているかどうかを確認します。
public class ServiceDiagnostics
{
readonly bool _isUserService;
readonly bool _isLocalSystem;
readonly bool _isInteractive;
public ServiceDiagnostics()
{
var wi = WindowsIdentity.GetCurrent();
var wp = new WindowsPrincipal(wi);
var serviceSid = new SecurityIdentifier(WellKnownSidType.ServiceSid, null);
var localSystemSid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
var interactiveSid = new SecurityIdentifier(WellKnownSidType.InteractiveSid, null);
this._isUserService = wp.IsInRole(serviceSid);
// Neither Interactive or Service was present in the current user's token, This implies
// that the process is running as a service, most likely running as LocalSystem.
this._isLocalSystem = wp.IsInRole(localSystemSid);
// This process has the Interactive SID in its token. This means that the process is
// running as a console process.
this._isInteractive = wp.IsInRole(interactiveSid);
}
public bool IsService
{
get { return this.IsUserService || this.IsLocalSystem || !this.IsInteractive; }
}
public bool IsConsole
{
get { return !this.IsService; }
}
/// <summary>
/// This process has the Service SID in its token. This means that the process is running
/// as a service running in a user account (not local system).
/// </summary>
public bool IsUserService
{
get { return this._isUserService; }
}
/// <summary>
/// Neither Interactive or Service was present in the current user's token, This implies
/// that the process is running as a service, most likely running as LocalSystem.
/// </summary>
public bool IsLocalSystem
{
get { return this._isLocalSystem; }
}
/// <summary>
/// This process has the Interactive SID in its token. This means that the process is
/// running as a console process.
/// </summary>
public bool IsInteractive
{
get { return this._isInteractive; }
}
}