views:

246

answers:

4

I'm porting some code from the full .NET framework to the WP7 version and I'm running into an issue with synchronous vs async calls.

 string response;
 string requestString = GenerateReqString();
 HttpWebRequest req = (HttpWebRequest) WebRequest.Create("endpoint");
 req.Method = "POST";
 req.ContentType = "text/xml";

 req.ContentLength = requestString.Length;

 StreamWriter sw = new StreamWriter (req.GetRequestStream(), System.Text.Encoding.ASCII);
 sw.Write(requestString);
 sw.Close();

 StreamReader sr = new StreamReader(req.GetResponse().GetResponseStream());
 response = sr.ReadToEnd();
 sr.Close();

The response string is then parsed into a list of objects that is returned by the method.

The problem I'm having is that there isn't a way to make the call synchronously in Silverlight/WP7. If I use a call back I'll get the response in a different function and wont be able to return it from the original function. Is there a way to either make the call synchronously or return from the CallBack function back to the method that kicked off the async call?

+6  A: 

You need to think about the problem differently. To make asynchronous things "feel" synchronous, the easiest way to do it is to restructure your code to make use of 'continuation passing style'.

In essence, instead of calling a function that returns a value and then you process that value, you call a function, passing an anonymous function as a delegate to it. The called function will then invoke the delegate, passing in the string.

Here is an example, which uses anonymous functions and lambdas:

void DoSomethingAsync( Action<string> callback ) {
    HttpWebRequest req; // TODO: build your request

    req.BeginGetResponse( result => {
        // This anonymous function is a closure and has access 
        // to the containing (or enclosing) function.
        var response = req.EndGetResponse( result );

        // Get the result string and call the callback
        string resultString = null; // TODO: read from the stream

        callback(resultString);
    }, null );
}

This is one half the solution. The next part, is to actually call this. Imagine you have an ICommand instance or simpler, a button click event that needed to call this function and "get the string". Instead of "getting the string" you call this function and supply a callback method (which will be a closure).

void btnGo_Click( object sender, EventArgs e ) {
    DoSomethingAsync( resultString => {
        // This anonymous function is called when the web request has
        // finished and has your string. 

        // Now that we have the string, we can go and process it.
        ProcessWebResponseResult( resultString );
    });
}

Here is a really good article explaining the concept further: http://blogs.msdn.com/b/wesdyer/archive/2007/12/22/continuation-passing-style.aspx

Adam Sills
Lets say ProcessWebResponseResult() creates an object from the string and the function that calls DoSomethingAsync() needs to return that object. Is that possible?
CACuzcatlan
That's missing the point. The method that calls DoSomethingAsync passes a function to DoSomethingAsync. The function passed is the "what to do with the result of this function". As you need more, you add additional continuation functions to your call chain.
Adam Sills
This is how you think of a program normally running: `A();B();C();`. However, when you execute with 'continuation passing style', you tell each function what to do when it's done. Instead of "I get my result from this function", you say "I tell this function what to do with its result" via a delegate.
Adam Sills
Perhaps Don Box talking on the issue may help: http://www.pluralsight-training.net/community/blogs/dbox/archive/2005/04/27/7780.aspx
Adam Sills
Updated the answer with a link to a great explanation.
Adam Sills
+1  A: 

Check out Wintellect's Power Threading library, which allows you to perform asynchronous operations with a synchronous programming model.

http://csharperimage.jeremylikness.com/2010/03/sequential-asynchronous-workflows-in.html

Phil
A: 

First I suggest trying to get comfortable with async but if you really want/need to convert an asynchronous call to a synchronous you can use ManualResetEvent to achieve the desired result.

Here is a quick example if usage:

public ManualResetEvent _event = new ManualResetEvent(false);

...
{
    ...
    var wc = new WebClient();
    wc.OpenReadCompleted += new OpenReadCompletedEventHandler(ReadCompleted);
    wc.OpenReadAsync(uri);

    // block until async call is complete
    _event.WaitOne();
}

private static void ReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    ...
    // set event to unblock caller
    _event.Set();
}

Now you code will block on the line _event.WaitOne(); until _event.Set(); is called.

Good Luck!

Jamey McElveen
A: 

Hi CACuzcatlan,

It's better to do this asynchronously so the user can continue to interact with the device.

I suspect the reason you wanted to do this is to prevent the user interacting with certain controls until your request is completed.

The accepted way to approach this is to hide or disable UI elements which should not be interacted with during the request processing.

Mick N