2011-10-25 241 views
1

我在VS 2010中有一個解決方案:一個WCF服務庫項目(「NotifyService」)和兩個Windows Forms項目,一個用於服務器(「NotifyServer」),另一個用於客戶端(「NotifyClient」)。我的目標是擁有一個雙工WCF服務,當服務器推出通知時,它將通知任何數量的連接客戶端。除了訂閱和取消訂閱服務器的更新外,客戶端不需要與服務器通信。然而,我似乎遇到了困擾InstanceContext的問題。分離出一個wcf服務,服務器和客戶端

下面是WCF服務的代碼:

<ServiceContract(
    CallbackContract:=GetType(INotifyCallback), 
    SessionMode:=SessionMode.Required)> 
Public Interface INotifyService 
    <OperationContract()> 
    Sub Notify(ByVal what As String) 

    <OperationContract()> 
    Sub Subscribe() 

    <OperationContract()> 
    Sub Unsubscribe() 

End Interface 

Public Interface INotifyCallback 
    <OperationContract(IsOneWay:=True)> 
    Sub OnNotify(ByVal what As String) 
End Interface 

<ServiceBehavior(
    ConcurrencyMode:=ConcurrencyMode.Single, 
    InstanceContextMode:=InstanceContextMode.PerCall)> 
Public Class NotifyService 
    Implements INotifyService 

    Private _callbacks As New List(Of INotifyCallback) 

    Public Sub Notify(ByVal what As String) Implements INotifyService.Notify 
     For Each callback As INotifyCallback In _callbacks 
      callback.OnNotify(what) 
     Next 
    End Sub 

    Public Sub Subscribe() Implements INotifyService.Subscribe 
     Dim client As INotifyCallback = OperationContext.Current.GetCallbackChannel(Of INotifyCallback)() 
     If Not _callbacks.Contains(client) Then 
      _callbacks.Add(client) 
     End If 
    End Sub 

    Public Sub Unsubscribe() Implements INotifyService.Unsubscribe 
     Dim client As INotifyCallback = OperationContext.Current.GetCallbackChannel(Of INotifyCallback)() 
     If _callbacks.Contains(client) Then 
      _callbacks.Remove(client) 
     End If 
    End Sub 
End Class 

服務器表格必須由服務庫和自主機上創建的DLL的引用代碼的WCF的服務器實例:

Public Class frmServer 
    Private _host As ServiceHost 
    Private _notifier As NotifyService.NotifyService 

    Public Sub go() Handles Me.Load 
     _host = New ServiceHost(GetType(NotifyService.NotifyService), New Uri("net.tcp://localhost:10000")) 
     _host.AddServiceEndpoint(GetType(NotifyService.INotifyService), New NetTcpBinding, "NotifyService") 
     _host.Description.Behaviors.Add(New ServiceMetadataBehavior) 
     _host.AddServiceEndpoint(GetType(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding, "mex") 
     _host.Open() 
     _notifier = New NotifyService.NotifyService 
    End Sub 

    Private Sub send() Handles Button1.Click 
     _notifier.Notify("Foo") 
    End Sub 
End Class 

大部分似乎都工作。我可以使用WcfTestClient進行連接,它至少可以看到服務,但由於它是一個啓用了雙工的net.tcp綁定,我實際上無法使用該客戶端對其進行測試。

當我創建客戶端時,我添加了名爲NotifyGateway的服務引用。這裏的客戶表單代碼:

<CallbackBehavior(
    ConcurrencyMode:=ConcurrencyMode.Single, 
    UseSynchronizationContext:=False)> 
Public Class frmClient 
    Implements NotifyGateway.INotifyServiceCallback 

    Private _service As NotifyGateway.NotifyServiceClient = Nothing 

    Public Sub OnNotify(ByVal what As String) Implements NotifyGateway.INotifyServiceCallback.OnNotify 
     MsgBox(what) 
    End Sub 

    Public Sub go() Handles Button1.Click 
     _service = New NotifyGateway.NotifyServiceClient(New InstanceContext(Me), New NetTcpBinding, New EndpointAddress("net.tcp://localhost:10000/NotifyService")) 
     _service.Open() 
     _service.Subscribe() 
    End Sub 
End Class 

由於我通過代碼設置了一切,所以沒有app.config文件。我遇到的問題是我的訂閱方法從未被調用過。在調試時,在遍歷該行(但不是例外)後出現錯誤,提示「無法自動進入服務器,調試器未能在服務器進程中停止」。當我回到我的服務器表單並單擊旨在在回調客戶端上引發事件的按鈕時,它會進入NotifyService類,但_callbacks列表爲空,這表示Subscribe方法從未運行過,或者它從未在該實例上運行過。

我已經打了近一個星期。這幾乎是一個完全相同的副本,至少在服務方面,與here描述的內容相同,並且我已經編譯並且它可以工作。所以我有點失落,因爲我要去哪裏錯...

回答

0

我認爲問題出在您的服務InstanceContextMode:=InstanceContextMode.PerCall。這意味着每次調用某個方法時都會創建一個新的服務實例。因此,當您撥打_notifier.Notify("Foo")時,它會創建您的服務類的新實例,這意味着它沒有任何訂閱者。還有,你可以接近這幾方面:

  • 的一種方法是設置InstanceContextMode:=InstanceContextMode.Single,這意味着將有服務類的一個實例將處理爲您服務的生命週期的所有請求。加上您的其他設置ConcurrencyMode:=ConcurrencyMode.Single,如果您期望您的服務被大量使用,這可能是一個瓶頸,因爲一次只能處理一個請求。您可以將其更改爲ConcurrencyMode:=ConcurrencyMode.Multiple,但是您必須負責處理類中的線程安全問題,因爲這允許您的類的單個實例同時處理多個請求。
  • 另一種方法是將您的_callbacks集合更改爲靜態(VB.NET中的Shared)成員。這樣,您的服務類別的所有實例中的訂戶列表將相同。在這種方法中,您將再次負責處理線程安全問題,因爲您的服務類將有多個實例可能會同時從不同線程訪問單個集合。
  • 您可以使用某種類型的持久性機制,例如數據庫。這將是最涉及的,因爲你基本上必須爲每個通知重新構建一個新的代理給每個訂閱者,但是如果你的應用程序被關閉,它的優點是不會失去所有的訂閱者。這聽起來像是它可能不適合你的情況。
+0

子彈點2做到了。由於我自己缺乏對C#到VB的轉換的關注。這不會太有用 - 只有極少數的計算機,最多,並在同一網絡上,所以(不要畏縮)我不擔心線程安全。共享成員將工作得很好。謝謝! – mounty

+0

然後我會用'InstanceContextMode.Single' /'ConcurrencyMode.Single'。如果你同時得到多個請求,這隻會減慢速度,那就是你將遇到併發問題。 –