2011-10-25 52 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'。如果你同时得到多个请求,这只会减慢速度,那就是你将遇到并发问题。 –