一定時間経過後にクライアントがサービスからコールバックできるように登録できるWCFタイマーサービスを作りたい。問題は、クライアントがコールバックされないことです。例外はスローされません。
コールバックインターフェイスは次のとおりです。
[ServiceContract]
public interface ITimerCallbackTarget
{
[OperationContract(IsOneWay = true)]
void OnTimeElapsed(int someInfo);
}
サービスは次のようになります。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class TimerService : ITimerService
private readonly Timer _timer = new Timer(2000); //System.Timers.Timer
public void Subscribe()
{
ITimerCallbackTarget listener =
OperationContext.Current.GetCallbackChannel<ITimerCallbackTarget>();
_timer.Elapsed += (p1, p2) =>
{
listener.OnTimeElapsed(999);
};
_timer.Start();
}
クライアントが使用するコールバックメソッドは次のとおりです。
private class TimerCallbackTarget : ITimerCallbackTarget
{
public void OnTimeElapsed(int someInfo)
{
Console.WriteLine(someInfo);
}
}
クライアントは次のように登録します。
private static void TestTimerService()
{
InstanceContext callbackInstance = new InstanceContext(new TimerCallbackTarget());
using (DuplexChannelFactory<ITimerService> dcf =
new DuplexChannelFactory<ITimerService>(callbackInstance,
"TimerService_SecureTcpEndpoint"))
{
ITimerService timerProxy = dcf.CreateChannel();
timerProxy.Subscribe();
}
}
タイマーなしのサブスクライブメソッドで別のスレッドを使用すると、次のように機能します。
ThreadPool.QueueUserWorkItem(p =>
{
listener.OnTimeElapsed(999);
});
サブスクライブメソッドの最後にThread.Sleep(3000)を配置すると、タイマー(3秒間)でも機能するため、サブスクライブメソッドの終了後にコールバックオブジェクトへのチャネルが閉じられる可能性があります。OperationContext.Current.GetCallbackChannel();で取得されたコールバックオブジェクトのクラススコープ変数を使用します。method-scope変数の代わりに役に立ちませんでした。
以前、タイマーサービスのタイマーの経過イベントハンドラーに新しいスレッドを作成して、高速化しようとしました。ObjectDisposedExceptionがスローされ、 「破棄されたオブジェクトにアクセスできません。オブジェクト名:'System.ServiceModel.Channels.ServiceChannel」というメッセージが表示されます。次に、サービスを単純化しようとしましたが、タイマーのみを使用しても説明どおりに問題が発生することがわかりましたが、例外は、クライアントのコールバックオブジェクトへの接続がどこかで失われたことを示していると思います。タイマースレッドで新しいスレッドを作成しないと、例外がないのは不思議です。コールバックメソッドは呼び出されません。