I have a solution in VS 2010: a WCF Service Library project (“NotifyService”) and two Windows Forms projects, one for a server (“NotifyServer”)and one for a client (“NotifyClient”). My goal is to have a duplex WCF service that will notify any number of connected clients when the server pushes out a notification. The clients do not need to communicate to the server except to subscribe and unsubscribe from the server’s updates. However I seem to be running into problems nailing down the InstanceContext.
Here’s the code for the WCF service:
<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
The Server form has a reference to the DLL created by the service library and self-hosts an instance of the WCF server in code:
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
For the most part that all seems to work. I can use the WcfTestClient to connect and it at least sees the services, though since it’s a net.tcp binding with duplex enabled I can’t actually test it using that client.
When I created the client I added a service reference called NotifyGateway. Here’s the client form code:
<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
Since I’m setting everything up through code, there are no app.config files. The problem I’m having is that my Subscribe method is never called. When debugging, I get an error after stepping through that line (not an exception) that says “Unable to automatically step into the server. The debugger failed to stop in the server process.” When I go back to my server form and click the button designed to raise an event on the callback clients, it steps into the NotifyService class but the _callbacks list is empty, meaning the Subscribe method either never ran, or it never ran on that instance.
I have fought with this for almost a week. This is nearly an identical copy, at least on the service-side, to what was described here, and I’ve compiled that and it works. So I’m a little lost as to where I’m going wrong…
I think the problem is with
InstanceContextMode:=InstanceContextMode.PerCallon your service. This means that a new instance of your service is created every time a method is invoked. So when you call_notifier.Notify("Foo"), it creates a new instance of your service class, which means it doesn’t have any subscribers. There are a few ways you can approach this:InstanceContextMode:=InstanceContextMode.Single, which means there will be a single instance of your service class which will handle all requests for the lifetime of your service. Together with your other setting ofConcurrencyMode:=ConcurrencyMode.Single, this can be a bottleneck if you expect your service to be used heavily, since only one request will ever be processed at a time. You can change it toConcurrencyMode:=ConcurrencyMode.Multiple, but then you’ll have to be responsible for handling thread-safety inside your class, since that allows the single instance of your class to handle multiple requests at the same time._callbackscollection to be a static (Shared in VB.NET) member. That way your list of subscribers would be the same across all instances of your service class. In this approach, you would again be responsible for handling thread-safety concerns, since there will be multiple instances of your service class potentially accessing the single collection at the same time from different threads.