0

I came up with an idea on how to fake SL WCF support for synchronous calls.

Basically,

private _completed;
private IList<Customer> _customers;
public void IList<Customer> GetAllCustomers()
{
    bool completed = false;
    DataServiceQuery<Customer> myQuery = this.Context.Customers;
    myQuery.BeginExecute(OnQueryExecuted, myQuery);

    while (!_completed)
      System.Threading.Thread.Sleep(67); //tried join also

    return _customers;
}

private void OnQueryExecuted(IAsyncResult result)
{
     var query = result.AsyncState as DataServiceQuery<Customer>;
    _customers = query.EndExecute(result).ToList();
    _isCompleted = true;
}

What happens is that this loops forever.

I put a break-point on the while loop, move it out of it and resume execution, and in the very next millisecond the results arrive.

So, I am thinking the callback for the query to receive the results gets queued to the same thread the query was invoked in.

SL seems very determined to maintain this behavior, so even if I wrap the myQuery.BeginExecute in a new thread, I still get the same behavior.

/*edit: actually, thinking about it, it queues the callback on the ui thread, which is waiting. this is also the reason we don't have to Dispatcher.Invoke when we get the results. anyhow, I can always do the whole operation (that requires the wait) in a dedicated thread, and then wait there, but this will require a bunch of refactoring, avoiding which is the point of trying this. */

Is there any way around this?

Servy
  • 202,030
  • 26
  • 332
  • 449
h.alex
  • 902
  • 1
  • 8
  • 31
  • you are blocking the UI thread one way or another, instead of looping around why not just call `Execute` ? This method `GetAllCustomers` is never going to be async anyway in the form that you have it now so just call `Execute` instead. I'm not quite getting why you try to involve in second thread instead of blocking just one thread - there is no benefit at all and it degrades you application performance. Could you elaborate a little more ? – user1416420 Feb 10 '13 at 02:09
  • the point is for it not to be async. the db call is async because of the `myQuery.BeginExecute`. anyhow, if I call `Execute`, Silverlight abruptly informs me that it does not support synchronous querying. – h.alex Feb 10 '13 at 02:11
  • oh, I see, I didn't know that, let me look around .. – user1416420 Feb 10 '13 at 02:12
  • Here is an [example](http://stackoverflow.com/questions/351644/synchronous-ado-net-dataservices-calls-in-silverlight) in your context, you just need 1 wait handle. – user1416420 Feb 10 '13 at 02:22
  • The Silverlight API isn't missing synchronous methods because the developers were lazy. They actively went around and took them out and went out of their way to prevent you from blocking the UI thread for long periods of time, which is exactly what you're trying to do. Rather than fighting the entire system and trying to do synchronous programming you need to use the async model and rely on non-blocking methods. That's the only way you're going to be able to effectively program in a Silverlight environment. – Servy Feb 10 '13 at 02:26
  • @user1416420 thanks for that link, it is useful. however, that post with the manual reset events provides only a great way to queue my logic after the data fetch is done. i can already do this, but like i said will require major re-factoring. – h.alex Feb 10 '13 at 11:35
  • @Servy blocking the UI thread is also bad in WPF/Win apps, but they didn't go out of their way to prevent it there. I'm not saying they are lazy, I'm saying they introduced major discrepancy between the two programming models, which are at least advertised to provide code sharing. It turns out that if you scratch one inch below the surface, this is false advertising. – h.alex Feb 10 '13 at 11:39
  • @h.alex You shouldn't be blocking the UI thread in a WPF/Winform application either, they just don't go out of their way to stop you from doing so. For any professional application you should be using an asynchronous model in any of those environments. – Servy Feb 10 '13 at 17:34
  • @Servy .. exactly, they don't try to stop you, they leave up to you what you will * decide* warrants a new thread and the associated rise in complexity, then they go and say..oh, you can't use this code in SL. – h.alex Feb 10 '13 at 17:39
  • @h.alex Part of that is because in WPF/Winform the entire application is yours. If you want to make it crappy it really only reflects on you. For SIlverlight, it's inside of a browser, so when you write low quality UI code that blocks the UI thread you make it appear as if the browser is broken; users are unlikely to realize that it's the Silverlight application inside of the page that is freezing their browser intentionally. It's the same thing with JavaScript; it goes out of it's way to ensure you can't block the UI thread. To be honest, I wish they did do the same thin in WPF/Winforms. – Servy Feb 10 '13 at 17:42
  • @Servy, you can block the ui thread. Just write `while(true) Thread.Sleep(1000); `. Point is it takes flexibility from the developer. – h.alex Feb 10 '13 at 17:48

1 Answers1

1

The reason this hangs is that the EndExecute method marshals to the UI thread, but you're blocking the UI thread with your Sleep call. It doesn't matter what technique you use to block the thread, anything you do is going to be causing a deadlock given that the method is called in a UI thread.

When developing in a SIlverlight environment you need to be able to program asynchronously, rather than using synchronous methods. If you are using C# 5.0 you can use the async/await functionality, which lets you write asynchronous code that appears synchronous, but it will be compiled into asynchronous calls that utilize callbacks/continuations, which is what you would need to do if you have not yet upgraded to that version.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • So, if they allowed blocking the UI thread in Win apps, why not in SL? After all, you will test, and if something hangs you will do it async. These things come back in under a 100 milliseconds anyhow. It's not just a matter of putting everything as continuations, now I have to do a concurrency check for a bunch of things. If the user clicked refresh some data list, then I should disable loading elements from it until it comes back.. etc. `async/await` is great, but doesn't solve this problem. – h.alex Feb 10 '13 at 11:49
  • And what do I gain? Zero! I can also limit my wait to say 133ms, declare the server unresponsive and show an error to the user.. For small round-trips. The larger ones I would, as any sane person, do async. But, ok, let bygones be bygones, and now let's do all our db trips async everywhere and always. – h.alex Feb 10 '13 at 11:54
  • @h.alex The point is that blocking the UI thread is freezing the entire application. You prevent paint events from being executed, you prevent the user from moving the browser around, you prevent them from navigating away or switching tabs. It's extremely detrimental to the user experience. – Servy Feb 10 '13 at 17:32