簡體   English   中英

實現異步WCF服務

[英]Implementing an async WCF service

我有一個WPF應用程序,我正在使用WCF分離到客戶端和服務器端。 我不喜歡我最初用直接解決方案得到的混亂,所以現在我正在根據MCFel Castro, WCF Extreme的截屏視頻中的建議進行重組。 如果您不熟悉該視頻,他基本上手動設置整個通信 - 不使用服務引用。 這包括:

  • 與所有服務和數據合同的通用合同 - 由客戶端和服務器引用
  • 托管服務的控制台應用程序
  • 客戶端上的代理類映射服務,並將調用傳遞給它(使用ClientBase或ClientFactory)

我已經完成了他所有的步驟,我真的很喜歡這里的發展方向。 但是,他沒有解決異步服務調用,這就是我想要使用的。

添加服務引用時,我可以選中“生成異步操作”復選框,然后獲取MyServiceCompleted和MyServiceAsync。 但是,我想這是在添加服務引用時生成的內容,而不是在構建的類中有一些魔力?

那么,我能以某種方式從ClientBase或ClientFactory獲取異步操作嗎? 或者我是否必須將實際的服務器端服務定義為異步? 如果是這樣 - 有人可以給我一些關於如何開始使用簡單異步服務的提示或示例嗎? 我一直在MSDN上閱讀這個主題,但它讓我感到很困惑,因為我已經沒有得到這個了...

在服務器端實現異步操作非常簡單。 確保方法名稱匹配,並以Begin和End為前綴。 GetImageAsyncResult是一個自定義的IAsyncResult實現(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();
                   }
              }
         }
    }

當您選擇“生成異步操作”時,正如您所指出的那樣,沒有任何魔力: Begin...將開始您的通信,服務器開始處理內容,但在您調用End...之前不能使用任何內容。

所有這些行為都發生在客戶端,因此您無需在服務實現中更改任何內容。

你可能認為這一定很復雜,但它沒有;)

編輯:舉個例子:

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

您還可以顯式編程您的服務以執行異步,如此處所述: 同步和異步操作 ,使用[OperationContract(AsyncPattern=true)]

在不使用svcutil的情況下在客戶端實現異步操作的另一種方法是在客戶端本地設置一個實現異步操作的接口(ServiceContract)。

服務器端合同:

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

客戶端的異步合同

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

請注意,需要在客戶端契約上設置名稱和默認命名空間,以匹配服務器契約的命名空間。 所以現在你有一個異步操作(希望沒有啟動任何新線程)。 這樣您就不必在服務器端執行任何特定實現。 當然,這類似於SvcUtil所做的,但SvcUtil會生成大量額外的代碼,有時我發現svcutil導致問題,例如重用類。

ClientProxy

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

客戶端的用法:

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

服務器類

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

服務器主機

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM