Saturday, 5 December 2009

Invoking WCF method Asynchronously and Signalling calling Thread

When calling WCF service method which takes longer to complete often you would require to have WCF client to be responsive while calling the method. Visual Studio allows developers to generate proxy for calling WCF service by adding service references. When generating service proxy Visual Studio also allows you to generate asynchronous method stubs to call WCF service method asynchronously.

Proxy creates two types of asynchronous methods stubs for clients to use. Consider following simple WCF calculator service:

	
[ServiceContract]
public interface ICalculatorService
{
[OperationContract]
float Add(float p1, float p2);

[OperationContract]
float Substract(float p1, float p2);
}

public class CalculatorService : ICalculatorService
{
#region ICalculatorService Members

public float Add(float p1, float p2)
{
Console.WriteLine("Adding {0} + {1}", p1, p2);

return p1 + p2;
}

public float Substract(float p1, float p2)
{
Console.WriteLine("Substracting {0} - {1}", p1, p2);

Thread.Sleep(5000);

return p1 - p2;
}

#endregion
}


At the client side the proxy generates couple of types of asynchronous method stubs. For instance for Add() we get following stubs:

   
public System.IAsyncResult BeginAdd(float p1, float p2, System.AsyncCallback callback, object asyncState)

public float EndAdd(System.IAsyncResult result)

private void OnAddCompleted(object state)

public void AddAsync(float p1, float p2)

public void AddAsync(float p1, float p2, object userState)


Many of us are familiar with BeginAdd() and EndAdd() methods. Same functionality can be achieved when using delegates. Similar methods are generated for Subtract(). Now the client can call any of these methods to asynchronously invoke WCF method. AddAsync() is something I found interesting. This method is used along with OnAddCompleted event handler which gets the return value from the method call. Here is how you can use this methods in client side.

        
static WaitHandle[] waits = new WaitHandle[] { new AutoResetEvent(false) };

static void Main(string[] args)
{
try
{
using (CalculatorServiceClient client = new CalculatorServiceClient())
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("Calling service methods asynchronously method 2");
client.SubstractCompleted += new EventHandler<SubstractCompletedEventArgs>(client_SubstractCompleted);
client.SubstractAsync(6, 3, waits[0]);
WaitHandle.WaitAll(waits);
}

Console.WriteLine("Press <ENTER> to terminate client");
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}

static void client_SubstractCompleted(object sender, SubstractCompletedEventArgs e)
{
Console.WriteLine("Async Substraction result = {0}", e.Result);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
((AutoResetEvent)e.UserState).Set();
}

Now I had to send WaitHandle[] as useState parameter to SubstractAsync() and also call WaitHandle.WaitAll(waits) after that. If we don’t do that client_SubstractCompleted() handler will throw TargetInvocationException(). The reason being SubstractAsync() takes longer to complete and before it completes using block disposes CalculatorServiceClient variable. To handle this situation our calling code needs to wait until the thread where subtract operation is taking place signals main thread about its completion.

For this we have created an array of WaitHandles and sent first array element as userState parameter of SubstractAsync(). The calling code waits until AutoResetEvent to signal main thread by calling Set() inside client_SubstractCompleted() before using block is completed. One thing to note here that client_SubstractCompleted() also executes in a separate thread. Thread.CurrentThread.ManagedThreadId outputs different values for both main thread and thread where subtract operation takes place.

No comments:

Post a Comment