views:

395

answers:

2

I have a WPF application which I'm currently working on separating into a client and server side - using WCF. I did not like the mess I initially got with the straight forward solution, so now I'm restructuring following the recommendations in the screencast of Miguel Castro, WCF Extreme. If you're not familiar with the video he basically sets up the whole communication manually - without using service references. This includes:

  • A common Contract with all service and data contracts - referenced by the client and server
  • A console application hosting the service
  • Proxy classes on the client mapping up the service, and passing calls to it (using ClientBase or ClientFactory)

I've followed all his steps, and I really like where this is going. However, he does not address asynchronous service calls, and this is what I'd like to use.

When adding a Service Reference I can check a "Generate async operations" checkbox, and I get MyServiceCompleted and MyServiceAsync. However, I guess this is something which was generated when adding the service reference, and not some magic in the classes this builds on?

So, can I get async operations from ClientBase or ClientFactory somehow? Or do I have to define the actual server side services to be async? If so - could someone please give me a few hints or examples on how to get started with a simple async service? I've been reading around on MSDN on the subject, but it has left me all confused feeling like an idiot for not getting this already..

+2  A: 

When you select "Generate async operations", as you noted, there is no magic involved: Begin... will start your communication, server starts processing stuff, but you can't use anything until you call End....

All this behavior happens at client-side, so you don't need to change anything at your service implementation.

You're probably thinking this must be complex, but it don't ;)

EDIT: Here go an example:

using (Service.SampleClient client = new Service.SampleClient())
{
    client.AddCompleted += 
        (object sender, Service.AddCompletedEventArgs e)
            {
                Console.WriteLine(e.Result); // 2
            };
    client.AddAsync(1, 1);

    // wait for async callback
    Console.ReadLine();
}

[ServiceContract]
public interface ISample
{
    [OperationContract]
    int Add(int a, int b);
}

You can also to explicitly program your service to work async, as describe here: Synchronous and Asynchronous Operations, by using [OperationContract(AsyncPattern=true)]

Rubens Farias
Ehr.. Could you please elaborate? You're saying I can leave the services untouched, and implement the async handling on the client side? Yeah - there is a high risk I'm seeing this as more complex then it is :-P
stiank81
ok, I'm working on a example, hold on =)
Rubens Farias
Thanks, appreciate it :-)
stiank81
I'm sorry for being slow, but what is Service.SampleClient anyway? Where did it get the AddCompleted and AddAsync functions from?
stiank81
Its just a sample code; There I declared that ISample interface...
Rubens Farias
+2  A: 

Implementing async operations on server side is quite simple. Make sure you method names match and are prefixed with Begin and End. GetImageAsyncResult is a custom IAsyncResult implementation (lots of examples on web).

    public class MapProvider : IMapProvider //implementation - belongs to server
    {
         public IAsyncResult BeginGetImage(int level, int x, int y, string[] layers, AsyncCallback callback, object state)
         {
              GetImageAsyncResult asyncResult = new GetImageAsyncResult(level, x, y, layers, callback, state);
              ThreadPool.QueueUserWorkItem(Callback, asyncResult);
              return asyncResult;
         }

         private void Callback(object state)
         {

              GetImageAsyncResult asyncResult = state as GetImageAsyncResult;
              asyncResult.Image = TileProvider.GetImage(asyncResult.Level, asyncResult.X, asyncResult.Y, asyncResult.Layers);
              asyncResult.Complete();
         }

         public System.Drawing.Bitmap EndGetImage(IAsyncResult result)
         {
              using (GetImageAsyncResult asyncResult = result as GetImageAsyncResult)
              {
                   asyncResult.AsyncWait.WaitOne();
                   return asyncResult.Image;
              }
         }
    }

    public class MapProviderProxy : ClientBase<IMapProvider>, IMapProvider, IDisposable
    {
         public IAsyncResult BeginGetImage(int level, int x, int y, string[] layers, AsyncCallback callback, object state)
         {
              return Channel.BeginGetImage(level, x, y, layers, callback, state);
         }

         public System.Drawing.Bitmap EndGetImage(IAsyncResult result)
         {
              return Channel.EndGetImage(result);
         }

         public void Dispose()
         {
              if (State == CommunicationState.Faulted)
              {
                   Abort();
              }
              else
              {
                   try
                   {
                        Close();
                   }
                   catch
                   {
                        Abort();
                   }
              }
         }
    }
Goran
Thanks! And the IMapProvider will have the BeginGetImage and EndGetImage functions defined - tagged with "[OperationContract(AsyncPattern = true)]"? And I need to define the specific result from this, GetImageAsyncResult?
stiank81
Correct - I found a base AsyncResult class on the web. GetImageAsyncResult derives from that.
Goran
Okay - this answers my question. However, I've realized that I don't want to make the service asynchronous, but I want to call the synchronous service asynchronously. I.e. I will handle it on the client side. I solve this easily using AsyncMethodCaller. Thanks anyway!
stiank81