Click here to Skip to main content
Click here to Skip to main content

Summarizing Client Side Asynchronous Invocations in WCF/WCF Silverlight

, 18 Apr 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Summarizing client side asynchronous invocations in WCF/WCF Silverlight. Introducing ServiceClient class.

Introduction

This article explains asynchronous calls in WCF and in WCF Silverlight with using runtime generated proxies based on exported service contracts, and also introduces ServiceClient<T> class to make asynchronous calls easier.

Asynchronous Operations in General

Asynchronous operation means that the actual operation is being executed parallel with the initiating thread that is free to continue its job while the operation is still in progress.

In terms of remote invocations, two main types of asynchronous calls can be differentiated. One is the server side, and the other one is the client side asynchronous call.

SERVER SIDE

In this case, the remote call from the client point of view is completely synchronous, it calls a method on a service that returns properly (remote call ended), so client can continue its work, but the main thing is that the server in the service method didn't finish the operation instead it just started a separate thread to perform it.

It is completely a server side solution; however the system needs to be designed considering this, because you don't get back the results at the end of the remote call. If client also wants results from the operation, the service should call back the client which requires extra facility (at this point, the client becomes the server) might not be simple (the client may need to open listening port, object references/delegates need to be passed to service: In .NET Remoting, you can pass object by reference (MarshalByRefObject) or a delegate targeting this kind of an object. WCF does not support passing objects by reference, you have to use duplexchannels (with bindings that support it).

GeneralA.png

CLIENT SIDE

We call client side asynchronous remote invocation when a service is called asynchronously from the client side, which is in turn entirely a client side solution. In fact, from the service point of view, the remote call is synchronous. So the worker thread executing the callback is launched on client side, it is completely a client side behaviour.

GeneralB.png

In this article, I only deal with this type of asynchronous calls.

Usual .NET Asynchronous Invocation Model (delegate.BeginInvoke)

I assume everyone is familiar with calling delegates asynchronously by using Delegate.BeginInvoke() method. In fact, if the method that the delegate targets is a method of a simple object, then it gets called on a thread from the CLR thread pool. But if it is a method of a proxy (WCF or Remoting), the proxy itself will provide the asynchronous facility to take advantage of the I/O Completion Ports.

In case of .NET Remoting, the realproxy is a System.Runtime.Remoting.Proxies.RemotingProxy, that calls AsyncProcessMessage on the channel. So the call will be asynchronous and calling thread returns immediately.

delegate User UserHandler(int id);

//obtaining proxy
IUserService srv = (IUserService)Activator.GetObject(typeof(IUserService), url); 

//build delegate on method
UserHandler uh = new UserHandler(srv.GetUser); 

//make async call
uh.BeginInvoke(id, null, null); 	//returns immediately, 
				//while operation is still in progress

What Happens in WCF?

In case of WCF, the realproxy is of type System.ServiceModel.Channels.ServiceChannelProxy. This proxy implementation calls service method synchronously even if we call it using BeginInvoke.

delegate User UserHandler(int id);

//obtaining proxy
IUserService srv = (IUserService)new ChannelFactory<IUserService>()<iuserservice />.CreateCannel(url);

//build delegate on method
UserHandler uh = new UserHandler(srv.GetUser);

//make async call
uh.BeginInvoke(id, null, null); //blocks the calling thread for entire period of call

WCF only issues asynchronous calls if the method that is called on a proxy begins with BeginXXX() and is decorated with [OperationContract(AsyncPattern=true)] attribute.

If you get your proxies generated (importing service contracts), both svcutil.exe and VS.NET can generate these BeginXXX methods for all your methods in the service, but if you use exported service contracts, the service contract (shared by service and client) of course will not contain these methods just the original service methods. See the next section.

Manually Reworking Service Interface (Contract) on Client Side, Adding BeginXXX, EndXXX Method Signatures

When using exported (shared) service contracts, all you can do is to manually rework the interfaces on client side (either by copying source code or derive another interface) and adding the necessary BeginXXX, EndXXX methods to the interface. You don't need to do anything else, as when calling BeginXXX, the proxy will in fact call XXX on the service but asynchronously.

[ServiceContract]
public interface IUserService
{
	[OperationContract()]
	User GetUser(int id);

	//Add this:
        [OperationContract(AsyncPattern=true)]
        IAsyncResult BeginGetUser(int id, AsyncCallback callback, object state);

        User EndGetUser(IAsyncResult asyncResult);
	//
}

And now you can simply call BeginGetUser:

IUserService srv = (IUserService)new ChannelFactory<IUserService>()<iuserservice />.CreateCannel(url);

srv.BeginGetUser(id, null, null); 	//now call returns immediately, 
				//while operation is in progress

Note that you didn’t even need the extra delegate.

Silverlight Difference/Solution

Among the many differences/deficiencies Silverlight has compared to .NET, there is one that in Silverlight you cannot call any WCF service synchronously. They really wanted to avoid any developer calling a service that blocks the main thread (in turn the browser), so only asynchronous calls are allowed. Considering that you cannot even use a .NET assembly in Silverlight, using the imported service contracts seems to be most convenient option.

However you still can use exported service contracts as well. You can only copy the source code of the service contracts to your Silverlight project, add BeginXXX, EndXXX method pairs for all methods, and delete original methods. The DataContracts can also be copied or just linked using VS.NET link file feature.

As you can see, it is not a big sacrifice to do it.

Using ServiceClient<T> Makes it a Bit Easier

Now on Silverlight client side, we have our reworked service interfaces in place with all the BeginXXX methods, so we can start using them.

However, at each service call we need to:

  1. Obtain a reference to the proxy
  2. Call the BeginXXX with parameters together with the AsyncCallback delegate
  3. Provide a handler for that delegate
  4. Call EndInvoke inside the handler to get the results
    and as we are on worker thread, we also need to
  5. Marshal to UI thread before we can perform the requested UI update.

To make our client application better readable and well structured, we can wrap these inside one class. For each service interface, we can have a class that encapsulates proxy creation and returns value extraction (on UI thread), so we don’t need to do these at each call.

The basic idea is to have a base class ServiceClient that takes the service interface as the T generic parameter and based on this, it automatically creates the proxy inside and stores it as a protected member. It also has a FireEvent() protected method that can call any delegate on the UI thread.

public class ServiceClient<T> : IDisposable where T : class
{
    protected T _proxy = null;

    //This looks up endpoint configuration with the name of type T 
    //as the  endpointname
    public ServiceClient() : this(typeof(T).Name)
    {
    }

    //This looks up endpoint configuration with given endpointName
    public ServiceClient(string endpointName)
    {
       //WCF proxy creation. 
       //If u use any IOC container to get service object reference, 
       //you can change this line to use that
        _proxy = new ChannelFactory<T>(endpointName).CreateChannel();
    }

    protected void FireEvent
		(Delegate del, bool autoMarshallToUI, params object[] args)
    {
        //fire outbound event
        if (!Deployment.Current.Dispatcher.CheckAccess() && autoMarshallToUI)
        {
            //on UI thread
            Deployment.Current.Dispatcher.BeginInvoke(del, args);
        }
        else
        {
            //on current thread
            del.DynamicInvoke(args);
        }
    }

    //........
    //....
}

Now, for each of your service interfaces you derive a class from this, passing the service interface as the T parameter, and for each service method you write a public AsyncXXX method and a private handler XXXCallback.

The AsyncXXX method gets the parameters and an arbitrary delegate where you want the results back. Inside it, just call the proxy with BeginXXX method passing an AsyncCallback on XXXCallback method, and in the object stateObject we pass our arbitrary delegate as well. So when call ends and XXXCallback is invoked, it calls EndInvoke on the proxy to get the result and then calls our arbitrary delegate on the UI thread using FireEvent with the result.

public class UserServiceClient : ServiceClient<IUserService><iuserservice />
{
    public UserServiceClient()
    {
    }

    public UserServiceClient(string endpointName) : base(endpointName)
    {
    }

    //1. GetUser
    public IAsyncResult AsyncGetUser(int id, Action<User><user /> cb)
    {
        return _proxy.BeginGetUser(id, GetUserCallback, cb);
    }

    private void GetUserCallback(IAsyncResult ar)
    {
        try
        {
            User user = _proxy.EndGetUser(ar);

            FireEvent((Delegate)ar.AsyncState, true, user);
        }
        catch (Exception ex)
        {
            //handle communication errors
            HandleError(ex);
        }
    }

    //2. GetUserByName
    //....
}

In this example, we use Action<User> as our custom callback delegate.

The base class also has a HandleError(Exception ex) method where you can put common error handling logic. (This is not shown here, but it is in the sample source code attached.)

This is a completely general solution, but with small changes it you can make it easier to work with your specific application. The best practice I think is to have customized base classes for each application derived from the ServiceClient.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Tamas Koszoru
Software Developer (Senior)
Ireland Ireland
.NET Developer.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberMichael Ulmann22-Aug-10 2:56 
GeneralSilverlight corrections PinmvpDaniel Vaughan17-Apr-10 0:07 
GeneralRe: Silverlight corrections PinmemberTamas Koszoru17-Apr-10 5:32 
GeneralRe: Silverlight corrections PinmvpDaniel Vaughan17-Apr-10 6:00 
GeneralRe: Silverlight corrections PinmvpDaniel Vaughan17-Apr-10 6:18 
GeneralRe: Silverlight corrections PinmemberTamas Koszoru17-Apr-10 8:07 
GeneralRe: Silverlight corrections PinmvpDaniel Vaughan17-Apr-10 9:15 
GeneralRe: Silverlight corrections PinmemberTamas Koszoru17-Apr-10 9:33 
GeneralRe: Silverlight corrections PinmemberTamas Koszoru17-Apr-10 9:45 
GeneralRe: Silverlight corrections PinmemberTamas Koszoru17-Apr-10 5:35 
GeneralRe: Silverlight corrections PinmvpDaniel Vaughan17-Apr-10 6:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.1411022.1 | Last Updated 18 Apr 2010
Article Copyright 2010 by Tamas Koszoru
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid