2009-12-11 210 views
6

我有一個WPF應用程序,我目前正在分離到客戶端和服務器端 - 使用WCF。我不喜歡我最初用簡單的解決方案所得到的爛攤子,所以現在我正在按照Miguel Castro的屏幕錄像中的建議進行重組,WCF Extreme。如果您對視頻不熟悉,他基本上會手動設置整個通信 - 無需使用服務參考。這包括:實現異步WCF服務

  • 所有服務和數據契約共用簽約 - 由客戶端和服務器引用
  • 託管服務的客戶端映射了服務
  • 代理類,並通過調用控制檯應用程序(使用ClientBase或ClientFactory)

我已經遵循了他的所有步驟,我真的很喜歡這裏。但是,他沒有提到異步服務調用,這就是我想要使用的。

在添加服務引用時,我可以檢查「生成異步操作」複選框,並且獲取MyServiceCompleted和MyServiceAsync。但是,我猜這是在添加服務引用時生成的,而不是在構建的類中存在某些魔法?

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

回答

5

在服務器端實現異步操作非常簡單。確保您的方法名稱匹配並以開始和結束爲前綴。 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(); 
        } 
       } 
     } 
    } 
+0

謝謝! IMapProvider將定義BeginGetImage和EndGetImage函數 - 標記爲「[OperationContract(AsyncPattern = true)]」?我需要從中定義具體的結果,GetImageAsyncResult? – stiank81

+0

正確 - 我在網上找到了一個基地AsyncResult類。 GetImageAsyncResult從中派生。 – Goran

+0

好的 - 這回答我的問題。但是,我意識到我不想使服務異步,但我想異步調用同步服務。即我會在客戶端處理它。我使用AsyncMethodCaller輕鬆解決這個問題。不管怎麼說,還是要謝謝你! – stiank81

3

當你選擇「生成異步操作」,如你所說,沒有所涉及的魔術: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); 
} 

您也可以明確地設定您的服務工作異步,如下所述:Synchronous and Asynchronous Operations,使用[OperationContract(AsyncPattern=true)]

+0

的Ehr ..能否請你解釋一下?你是說我可以離開服務不變,並執行異步處理在客戶端?是的 - 有一個高風險的我看到這是更復雜的則是:-P – stiank81

+0

好,我工作的一個例子,按住=) –

+0

感謝,感激:-) – stiank81

3

一種替代的方式來實現在客戶端異步操作,而不使用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(); 
}