开发者

Implementing an async WCF service

开发者 https://www.devze.com 2022-12-14 00:08 出处:网络
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 r

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 fol开发者_Python百科lowing 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..


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();
                   }
              }
         }
    }


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)]


An alternate way to achieve async operations on the client side without using the svcutil is to set up an interface (ServiceContract) locally on the client side which implements the async operation.

Contract on the server side:

[ServiceContract]
public interface IServerContract
{
    [OperationContract]
    string GetData(int value);
 }

Async contract on the client side

[ServiceContract(Namespace = "http://tempuri.org/", Name = "IServerContract")]
 public interface IClientContractAsync
 {
      [OperationContract] 
      Task<string> GetDataAsync(int value);
 }

Note that the name and the default namespace needs to be set on the client contract in order to match the server contract's namespace. So now you have an async operation (without starting any new threads hopefully). This way you do not have to do any specific implementation on the server side. Off course this is similar to what the SvcUtil does but SvcUtil generates a lot of extra code and sometimes I find svcutil causing problems e.g. with the reuse of classes.

The ClientProxy

public class ClientProxy : ClientBase<IClientContractAsync>
{
    public IClientContractAsync ChannelOut
    {
        get
        {
            return Channel;
        }
    }
}

The usage of the client:

static void Main(string[] args)
{
    var client = new ClientProxy();
    client.Open();
    var asyncReadValue = client.ChannelOut.GetDataAsync(45).Result;
    Console.WriteLine(asyncReadValue);
    Console.ReadLine();
}

The server class

public class ServerService : IServerContract
{
    public string GetData(int value)
    {
        return string.Format("You entered: {0}", value);
    }
}

The server host

static void Main(string[] args)
{
    var server = new ServiceHost(typeof(ServerService));
    server.Open();
    Console.WriteLine("started");
    Console.ReadLine();
}
0

精彩评论

暂无评论...
验证码 换一张
取 消