Serhii Kyslyi
Serhii Kyslyi

Reputation: 1823

How to return info from Callback method? C# 4.0

I am using WCF Async methods.

I face issue when I try to return the value of the callback function.

What are the possible methods of solution? (using .net 4.0, but not 4.5)

    public static Object LoadInfo()
    {
        var service = new SomeWcfService();
        service.BeginGetInfo(CallbackMethod, service);

        // HOW TO GET INFROMATION FROM CALLBACK??
        return INFORMATION;
    }

    private static void CallbackMethod(IAsyncResult ar)
    {
        // HOW TO PASS INFROMATION TO LoadInfo??
        var INFORMATION = (ar.AsyncState as SomeWcfService).EndGetInfo(ar);
    }

Note: All work should be asynchronously.

Thanks.

Upvotes: 1

Views: 3226

Answers (2)

EtherDragon
EtherDragon

Reputation: 2698

Understanding async at first, especially with callbacks is tough. In your example you make an incorrect, but natural assumption...

public static Object LoadInfo() 
{ 
    var service = new SomeWcfService(); 
    service.BeginGetInfo(CallbackMethod, service); 

    // HOW TO GET INFROMATION FROM CALLBACK?? 
    // ERROR: You assume you have more work to do in this method,
    // or that this is the place to return your results.
    return INFORMATION; 
} 

The method you give below, is where the work occurs after your results are returned:

private static void CallbackMethod(IAsyncResult ar) 
{ 
    // HOW TO PASS INFROMATION TO LoadInfo?? 
    // OOPS! No need to pass pack to LoadInfo - it's done...
    var INFORMATION = (ar.AsyncState as SomeWcfService).EndGetInfo(ar); 
}

Instead you will want something like this

public static void LoadInfo()        
{        
    var service = new SomeWcfService();        
    // begin an asynchronous service call
    // and handle the results in another method, "CallbackMethod"
    service.BeginGetInfo(CallbackMethod, service);  
    // You can do other, non-service related,
    // things here while the service call executes 
}   

Then your other method handles all the results:

private static void CallbackMethod(IAsyncResult ar)            
{            
    var results = (ar.AsyncState as SomeWcfService).EndGetInfo(ar);  
    // Do whetever you need with results here
}    

As Will pointed out in his excelent answer (+1, as if he needs it lol!), instead of having a separate call-back method, you can use an anonymous method with a lambda expression like:

public static void LoadInfo()        
{        
    var service = new SomeWcfService();        
    // begin an asynchronous service call
    // and handle the results in this anonymous method
    service.BeginGetInfo(x =>
    {
        // B. This code block will be called when the service returns with results
        var results = (ar.AsyncState as SomeWcfService).EndGetInfo(ar);  
        // Do whetever you need with results here
    }, service);  

    // A. You can do other things here while the service call executes
    // but debugging this gets more complicated because things will likely
    // occur at A before they occur at B
}

So, the over-all mentality of Asynchronous is:

  • Your program sets up and begins a service call then keeps doing whatever else it wants, without waiting. Hint: This is a natural place to start a loading animation, and/or disable some controls.
  • When you made the async call, you gave, as one of your parameters, a method to run once the call is complete.
  • When the service returns with results, the method you specified is run to do work on the results. Hint: In this method, you can end the loading animation, and/or re-enable your controls, and plug the results into your ViewModel.

Upvotes: 1

user1228
user1228

Reputation:

Better you design to use the async pattern than fight against it.

But, if you must access an async method synchronously, you can use a semaphore to block the calling thread until the async method returns.

public static Object LoadInfo()
{
    // this is our semaphore
    var blocker = new AutoResetEvent();
    object result = null;

    var service = new SomeWcfService();
    // use a lambda instead of a method as the callback.
    // this will result in a closure where we can access the result and blocker variables
    service.BeginGetInfo(x => 
    {
        // We are on a different thread within this lambda
        result = (x.AsyncState as SomeWcfService).EndGetInfo(ar);
        // release anybody calling blocker.WaitOne
        blocker.Set(); 
    }, service);

    // we are still on the original thread here, and
    // BeginGetInfo has possibly not yet executed, so we must wait until Set is called
    blocker.WaitOne(Timeout.Infinite); 
    return result;
}

This is rarely a good design choice. Aynchornous patterns are much better for responsive UI. Better to do something like this

public void LoadInfo()
{
    // Makes the UI show a loading indicator, blocking all actions except for CANCEL
    LoadingInfo = true;

    var service = new SomeWcfService();
    service.BeginGetInfo(CallbackMethod, service);
}

private void CallbackMethod(IAsyncResult ar)
{
    // the UI is now released from loading 
    LoadingInfo = false;
    // the UI is triggered to show our data
    ViewModel.Data = (ar.AsyncState as SomeWcfService).EndGetInfo(ar);
}

Upvotes: 1

Related Questions