views:

339

answers:

3

I'm in the middle of a Silverlight application and I have a function which needs to call a webservice and using the result complete the rest of the function.

My issue is that I would have normally done a synchronous web service call got the result and using that carried on with the function. As Silverlight doesn't support synchronous web service calls without additional custom classes to mimic it, I figure it would be best to go with the flow of async rather than fight it. So my question relates around whats the best design pattern for working with async calls in program flow.

In the following example I want to use the myFunction TypeId parameter depending on the return value of the web service call. But I don't want to call the web service until this function is called. How can I alter my code design to allow for the async call?

        string _myPath;

    bool myFunction(Guid TypeId)
    {
        WS_WebService1.WS_WebService1SoapClient proxy = new WS_WebService1.WS_WebService1SoapClient();
        proxy.GetPathByTypeIdCompleted += new System.EventHandler<WS_WebService1.GetPathByTypeIdCompleted>(proxy_GetPathByTypeIdCompleted);
        proxy.GetPathByTypeIdAsync(TypeId);

        // Get return value

        if (myPath == "\\Server1")
        {
            //Use the TypeId parameter in here
        }
    }

    void proxy_GetPathByTypeIdCompleted(object sender, WS_WebService1.GetPathByTypeIdCompletedEventArgs e)
    {
        string server = e.Result.Server;
        myPath = '\\' + server;
    }

Thanks in advance, Mike

+2  A: 

Given the asynch nature of Silverlight you cannot return values from myFunction. Instead you can pass an Action which is executed once the service call is complete. See the example code below. I am not sure if it is considered best practice, but I use this "pattern" a lot and it has always worked fine for me.

EDIT
Updated the code below to include multiple arguments in the callback action.

void DoSomething(Guid TypeId, Action<int, bool> Callback)
{
    WS_WebService1.WS_WebService1SoapClient proxy = new WS_WebService1.WS_WebService1SoapClient();
    proxy.GetPathByTypeIdCompleted += (s, e) =>
        {
            string server = e.Result.Server;
            myPath = '\\' + server;

            //
            if (myPath == "\\Server1")
            {
                Callback(888, true);
            }
            else
            {
                Callback(999, false);
            }
        };
    proxy.GetPathByTypeIdAsync(TypeId);

}

void CallDoSomething()
{
    DoSomething(Guid.NewGuid(), (returnValue1, returnValue2) =>
        {
            //Here you can do stuff with the returned value(s)
        });
}
Henrik Söderlund
This example seems pretty good to me, is it possible to pass multiple variables through the Action parameter?Example:CallBack(true, myObject);Thanks.
Mike Mengell
Sure, I just updated the sample code above to include an int and a bool as parameters. But you can change it to whatever you need.
Henrik Söderlund
+1  A: 

Put the processing of the GetPathByTypeId result into the GetPathByTypeIdCompleted callback. Assign mypath there. Make mypath a property and implement the INotifyPropertyChanged interface to notify dependents of Mypath that Mypath has changed.

  1. Observer depends on mypath
  2. Observer sets a notification event for mypath
  3. Get Mypath by asynchronous invocation of GetPathByTypeId
  4. Mypath is set, invokes notifiaction of Observer
  5. Observer works with Mypath
Ozan
I came to the same idea and I have implemented this method. It's ok but it just doesn't seem very elegant to me. But thank you for helping.
Mike Mengell
+2  A: 

The best would be to use Reactive Extensions. Then (assuming you'd create an extension method IObservable<string> GetPathByTypeId(string typeId) on WS_WebService1SoapClient you can do this:

proxy
    .GetPathByTypeId(TypeId)
    .Subscribe(server => 
        { 
            //Here you can do stuff with the returned value
        });

As close to having synchronous call as it gets :)

PL
Thank you very much I'll take a look
Mike Mengell