I want to make a WCF timer service where clients can register in order to get called back from the service after a certain time has passed. The problem is that the client doesn't get called back. No Exception is thrown.
The callback interface is:
[ServiceContract]
public interface ITimerCallbackTarget
{
[OperationContract(IsOneWay = true)]
void OnTimeElapsed(int someInfo);
}
The service looks like:
[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();
}
The callback method used by the client is:
private class TimerCallbackTarget : ITimerCallbackTarget
{
public void OnTimeElapsed(int someInfo)
{
Console.WriteLine(someInfo);
}
}
The client registers like this:
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();
}
}
If I use a different thread at the subscribe method without Timer it works:
ThreadPool.QueueUserWorkItem(p =>
{
listener.OnTimeElapsed(999);
});
It even works with the Timer (for three seconds) if I put a Thread.Sleep(3000) at the end of the subscribe method so my guess is that maybe the channel to the callback-object gets closed after the subscribe method is finished. Using a class-scope variable for the callback object retrieved with OperationContext.Current.GetCallbackChannel(); instead of the method-scope variable didn't help.
Previously i tried creating new Threads in the elapsed event handler of the Timer of the timer service to make it faster. An ObjectDisposedException was thrown with the message: "Cannot access a disposed object. Object name: 'System.ServiceModel.Channels.ServiceChannel". I then tried to simplify my service and found that even using only the Timer causes problems as described but I guess the exception indicates that somewhere the connection to the client's callback object is lost. It's strange that there is no excepiton if I don't make new threads in the Timer thread. The callback method just isn't called.