Question

I have created a WCF Service Application and basically changed nothing except names, the default contract is there and contains a method GetData(Int32) that returns a String.

Then I have created WPF client application targeting .NET FW 4.5 so where I add service reference to said service, I can choose to include Task-based asynchronous variant of contract methods in generated service client.

Now when I try to use the service, like this:

  using (var client = new RhaeoServiceClient())
  {
    client.GetDataAsync(17).ContinueWith(t => MessageBox.Show(t.Result));
    MessageBox.Show("inb4");
  }

When executed from Button click handler, the window stalls, button remains pressed for a second or so, and then "inb4" message shows, so it seems to me that task runs on main thread and waits for network, thus freezes the UI.

The "inb4" doesn't show immediately after click, it seems to wait after task is executed, like I said, a sec or two. After "inb4" shows, next message box with result shows as well, but what's funny to me is then next message box doesn't wait on me to close the first one, it just pops over the first one almost immediately after the first one is show.

So that's confusing, because it makes it seems like the continuation code is in fact run in different thread and doesn't care that main thread is blocked by the first message box. But how does it display the message box, which can only be shown from UI thread (right?)?

And why does the first message box wait after task executes, then show, and then by covered by the next one without being closed?

Was it helpful?

Solution

The t.Result will block the calling thread until the Task completes. You can achieve the result you want by using the async/await keyword in conjunction with your WCF call.

    private void button1_Click(object sender, EventArgs e)
    {
        CallGetDataAsync(17);
        MessageBox.Show("inb4");
    }

    private async void CallGetDataAsync(int number)
    {
        string result = null;

        var client = new Service1Client();
        try
        {
            // After this line, control is returned to the calling method; the ConfigureAwait(true) 
            // explicitly indicates that when execution resumes, it should attempt to marshall back
            // to the calling thread.  If you change it to false, you can see that the subsequent
            // messagebox does not stop you from interacting with your main form.
            result = await client.GetDataAsync(number).ConfigureAwait(true);

            // when the async service call completes, execution will resume here
            client.Close();
        }
        catch
        {
            try
            {
                client.Close();
            }
            catch
            {
                client.Abort();
            }
            throw;
        }

        // display the MessageBox, this should block the UI thread 
        MessageBox.Show(result);
    }

Running this against a service running on the same machine as the client, it is hard to see what is going on because the WCF service call will return fast enough that your "inb4" is still displayed after service result message. Putting a delay in the sample service method helps to illustrate the behavior better.

    public string GetData(int value)
    {
        return Task.Delay(TimeSpan.FromSeconds(5)).ContinueWith(_ => string.Format("You entered: {0}", value)).Result;
    }

To your last question, a MessageBox can be invoked by a background thread. However, it won't act as a modal and block your main form if done so.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top