Introduction
The Publisher/Subscriber model is where the Subscriber is the service to which all clients subscribe and the Publisher is the service which sends messages to all clients who have subscribed to the Subscriber service. That is, suppose we have 10 clients and all 10 clients would subscribe to the Subscriber service to receive notifications (client programs are individual programs), and then whenever the required event occurs, the Publisher notifies all those clients registered.
Background
After a thorough reading done here, I understood how the Publisher/Subscriber model is implemented. To know more about this model, have a look here. The reading should let you understand this model.
Then, I went on exploring the Windows Vista SDK, where I found the List Publisher/Subscribe sample. Exploring the same, I was able to understand the logic.
Here is a pictorial representation:
To explain briefly:
- We have a WCF Service (the above diagram tells that the WCF Service is hosted in IIS)
- Design the callback contract between the Service and the Client
- Setup a Delegate and Event, and the delegate method handler is going to raise that particular event
- So, when clients subscribe, we increment the event handler which would point to the client’s callback function
- Now, we build a separate datasource which implements the service callback contract
- This datasource is responsible to call the Publish function
- The Publish function is just going to raise the event, and thus every callback in the Event will be called now
Using the code
I have mainly built three components:
- PubSubService
- PubSubClient
- Publisher
PubSubService
This is our main Service which allows the Client to Subscribe, Unsubscribe, and the Publisher to Publish messages to the subscribed clients. Our Service also has a CallBackContract
which is going to be our source at the Client for notifications sent out by the Service.
Below is our ServiceContract
interface:
[ServiceContract(Namespace = "http://ListPublishSubscribe.Service",
SessionMode = SessionMode.Required, CallbackContract = typeof
(IPubSubContract))]
public interface IPubSubService
{
[OperationContract(IsOneWay = false, IsInitiating=true)]
void Subscribe();
[OperationContract(IsOneWay = false, IsInitiating=true)]
void Unsubscribe();
[OperationContract(IsOneWay = false)]
void PublishNameChange(string Name);
}
It is pretty clear what we have. Our ServiceContract
SessionType
is set to be SessionMode.Required
; so, you have to connect the client, and a session is required and it has three OperationContract
s namely:
Subscribe
UnSubscribe
PublishNameChange
The IsOneWay
tells that it's not a one way contract, and it's a two way contract meaning that this contract can be called by the Client as well as return some values back to the Client. IsInitiating
tells that this contract can initiate a session in the Server/Service.
We have specified the interface IPubSubContract
as our CallbackContract
. This means that this would be a medium of data exchange between the Service and Client, and using these Callbacks, we can actually make the Service notify the Clients regarding specific events.
Below is our IPubSubContract
interface:
public interface IPubSubContract
{
[OperationContract(IsOneWay = true)]
void NameChange(string Name);
}
We just have one OperationContract
called NameChange
. We would be going into this soon.
As now we have our Interfaces defined, it is time to implement our ServiceContract
, and below is the class which implements the above IPubSubService
interface.
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class PubSubService : IPubSubService
{
public delegate void NameChangeEventHandler(object sender, ServiceEventArgs e);
public static event NameChangeEventHandler NameChangeEvent;
IPubSubContract ServiceCallback = null;
NameChangeEventHandler NameHandler = null;
public void Subscribe()
{
ServiceCallback = OperationContext.Current.GetCallbackChannel<IPubSubContract>();
NameHandler = new NameChangeEventHandler(PublishNameChangeHandler);
NameChangeEvent += NameHandler;
}
public void Unsubscribe()
{
NameChangeEvent -= NameHandler;
}
public void PublishNameChange(string Name)
{
ServiceEventArgs se = new ServiceEventArgs();
se.Name = Name;
NameChangeEvent(this, se);
}
public void PublishNameChangeHandler(object sender,ServiceEventArgs se)
{
ServiceCallback.NameChange(se.Name);
}
}
We have implemented our already defined three OperationContract
s, and also we have defined some delegates and events. What are those?
The logic is simple. Whenever a Client subscribes, we create an event handler and add to our event which is NameChangeEvent
. So, what happens here, if “n” number of Clients join, we have “n” number of event handlers referring to those Clients.
How is this accomplished?
Simple. Create a Delegate, associate an event to it, and in the DelegateHandler
, we call the ServiceCallback
’s NameChange
contract when we want to Publish, and since it is a CallbackContract
and implemented in the Client, this function would be invoked in the Client, and thus we have published our message to the Client.
So, if you look in the PublishNameChangeHandler
, you could see that we invoke the ServiceCallback.NameChange
, which makes it clear.
I have hosted the PubSubService in IIS to make things simpler.
Now, let's move on to our PubSubClient.
PubSubClient
Here, we are going to add our WCF Service, implement the IPubSubContract
interface to get the Callback from the Service, subscribe to the Service, and be ready to receive messages when our Publisher publishes them.
Add your PubSubService through the Add Service Reference. If you are not familiar, have a look at my post regarding adding a WCF Service to your Application.
Our PubSubClient is a normal Windows Forms Application, having just a Label
in the Form, and this Label
will be updated when a message is Published by the Publisher.
I have used the PubSubService as the reference for my Service. Once you add the PubSubService, we get our PubSubService.map and PubSubService.cs through which now you can implement the IPubSubContract
.
[CallbackBehaviorAttribute(UseSynchronizationContext = false)]
public class ServiceCallback : IPubSubServiceCallback
{
public void NameChange(string Name)
{
Client.MyEventCallbackEvent(Name);
}
}
Our implementation is straightforward. We implement the NameChange
contract here at our Client, and now you should be able to bring out what I meant earlier. This way, when you call from the Service using the Callback, the function in the Client would be called.
What have I done in my NameChange
function above?
I have done Marshaling. Marshaling is used when you want to access variables in another environment or thread. And since our Windows Forms is running as a separate thread, to use safe threading, I have used Marshaling here.
Implementing Marshaling is simple. We create a Delegate and associate an Event to it and make the event static
. We raise the event from the Service Callback, and update the Label
control in our Form.
Below is our client class, and you can see all the Delegates, Events, and also our ServiceCallback
implementation done here.
public partial class Client : Form
{
public delegate void MyEventCallbackHandler(string Name);
public static event MyEventCallbackHandler MyEventCallbackEvent;
delegate void SafeThreadCheck(string Name);
[CallbackBehaviorAttribute(UseSynchronizationContext = false)]
public class ServiceCallback : IPubSubServiceCallback
{
public void NameChange(string Name)
{
Client.MyEventCallbackEvent(Name);
}
}
public Client()
{
InitializeComponent();
InstanceContext context = new InstanceContext(new ServiceCallback());
PubSubServiceClient client = new PubSubServiceClient(context);
MyEventCallbackHandler callbackHandler = new MyEventCallbackHandler(UpdateForm);
MyEventCallbackEvent += callbackHandler;
client.Subscribe();
}
public void UpdateForm(string Name)
{
if (lblDisplay.InvokeRequired)
{
SafeThreadCheck sc = new SafeThreadCheck(UpdateForm);
this.BeginInvoke(sc, new object[] { Name });
}
else
{
lblDisplay.Text += Name;
}
}
}
The only thing to keep in mind is the Marshaling. Here, I use the BeginInvoke
method so that it doesn't wait for that delegate handler to complete, and the UI thread continues to do its work. If you use the Invoke
method, the UI thread waits till its operation is completed. It actually depends on whether to use BeginInvoke
or Invoke
. You can also use Invoke
here in the place of BeginInvoke
.
Publisher
Publisher is the most simplest of all. We add our PubSubService
, create the Service object, and just invoke the PublishNameChange
contract, and thus that would invoke the ServiceCallback
’s NameChange
contract to the subscribed Clients, and thus exchanging the data with the Clients.
Below is the Publisher code:
public partial class Publisher : Form
{
InstanceContext context = null;
PubSubServiceClient client = null;
public class ServiceCallback : IPubSubServiceCallback
{
public void NameChange(string Name)
{
MessageBox.Show(Name);
}
}
public Publisher()
{
InitializeComponent();
}
private void btnPublish_Click(object sender, EventArgs e)
{
context = new InstanceContext(new ServiceCallback());
client = new PubSubServiceClient(context);
client.PublishNameChange(txtMessage.Text);
client.Close();
}
}
Now, we are ready to subscribe and publish ;)
Hope you had a good time reading my article on how to implement Publisher/Subscriber using WCF. If you have any queries, please feel free to drop me an email or start a discussion below.
History
- Jan 10,2008 - Added the article.