-2

I am using an API provided by a data vendor to pull data into a C#/WPF application. I have the connections working fine and data is pulling. I put in a check to verify periodically that connection has not been lost and reconnect if that happens. That check gets executed by a timer but it doesn't work for some reason even though it's the same code that gets called and works anywhere else in my application.

I think it has something to do with the fact that there's a singleton instance and the timer is a separate thread. But I'm not sure how to fix it.

This gets called after InitializeComponent

public void OpenConnections()
{
    DataServices.Instance.StateChanged += OnStateChanged;
    DataServices.Instance.Initialize(AppName);
    _timeSeries = DataServices.Instance.TimeSeries;
    _timeSeries.ServiceInformationChanged += 
    timeSeries_ServiceInformationChanged;
}

The event handlers OnStateChanged and timeSeries_ServiceInformationChanged just update the UI to show the user the connection status.

These 4 lines work fine when called upon startup or where from the UI via a "reconnect" button or anything like that but not via an elapsed timer event, even if I use the lock keyword as such:

private static readonly object padlock = new object();

private void OnTimedEvent(object source, ElapsedEventArgs e)
{
    if (DataServices.Instance.State == DataServicesState.Up && 
        _timeSeries.ServiceInformation.State == ServiceState.Up)
    {
        //do nothing, services are up and connected.
    }
    else
    {
        lock(padlock)
        {
            OpenConnections();
        }
    }
}

EDIT:

Here is the timer code, as requested.

System.Timers.Timer ConnectionCheckerTimer;


private void InitializeConnectionCheckerTimer()
{
    ConnectionCheckerTimer = new System.Timers.Timer(5000);
    ConnectionCheckerTimer.Elapsed += OnTimedEvent;
    ConnectionCheckerTimer.AutoReset = true;
    ConnectionCheckerTimer.Enabled = true;
}

And InitializeConnectionCheckerTimer() is called after InitalizeComponent()

EDIT #2 I got it to work. The issue was a threading problem and the API will only connect from the UI thread which created it. I'm not sure why this is, but the way around it is by calling the code we want directly from the UI thread by manually invoking as follows

private void OnTimedEvent(object source, ElapsedEventArgs e)
{
    if (DataServices.Instance.State == DataServicesState.Up && 
        _timeSeries.ServiceInformation.State == ServiceState.Up)
    {

    }
    else
    {
        Application.Current.Dispatcher.Invoke(
            () =>
            {
                OpenConnections();
            });
    }
}
turc1656
  • 105
  • 2
  • 6
  • Where is you "timer" code? Like, where do you hook up the event? You have shown the method `OnTimeEvent` but not where it is configured. Could you show the code please? – Simon Wilson Aug 22 '19 at 15:24
  • 1
    Is the timer event being raised on the gui thread, or on some other thread? – Sean Aug 22 '19 at 15:24
  • I edited the question to show the timer code. I've stepped through the debugger and verified that the code is indeed getting triggered, as expected. So the timer is going off and the event is triggering the appropriate code. I just can't imagine why the result is different than when it is called from the UI thread. – turc1656 Aug 22 '19 at 15:30
  • Ah, just saw the "UI thread" comment. Maybe it's not my initial suggestion – Simon Wilson Aug 22 '19 at 15:30
  • So, what doesn't work exactly? `OnTimedEvent` is firing? `OpenConnections` is being called? So what doesn't work about it? – Joshua Robinson Aug 22 '19 at 15:34
  • OnTimedEvent is firing and OpenConnections is being called and everything appears to be executing as it should. The problem is that the results is not what is expected. Basically nothing happens and the connections aren't re-obtained. It remains disconnected but this only seems to happen on the timer thread. – turc1656 Aug 22 '19 at 15:41
  • Correction - it only works on the UI thread. I have a background worker that executes most of the code because it takes a while to download all the data from the API. Putting the OpenConnections() in the method that the background worker calls also does not reconnect as expected. It only works on the UI thread so it's definitely a threading issue but I don't know what is causing this. – turc1656 Aug 22 '19 at 15:42

1 Answers1

-1

I was able to get it to work by manually invoking the UI thread's Dispatcher and executing code from there.

private void OnTimedEvent(object source, ElapsedEventArgs e)
{
    if (DataServices.Instance.State == DataServicesState.Up && _timeSeries.ServiceInformation.State == ServiceState.Up)
    {

    }
    else
    {
        Application.Current.Dispatcher.Invoke(
            () =>
            {
                OpenConnections();
            });
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
turc1656
  • 105
  • 2
  • 6