Talking Without Speaking, Hearing Without Listening.

In this blog I publish information and ideally have people that subscribe to it. For the blog I use the WordPress hosting site (service) you may use an RSS aggregator to subscribe (client). In a business application you can achieve this same concept easily with WCF. In this post I will run through an example of the Medical System Client application subscribing to patients of interest. When other clients update those same patients with business critical data the active clients can receive an ‘alert’ immediately at easily.

We begin with the client side setup. First we define the contract interface that contains our operational methods (excluded below, but examples include Save(), Retrieve(), etc). The key things to note on the interface is the ‘CallbackContract’ attribute, and the subscribe/unsubscribe methods. The subscription methods use a Patient Id key but this can be whatever is most suitable in your situation, for example an enum will work fine for more generalised event types.

[ServiceContract(CallbackContract = typeof(IPatientEvents))]
public interface IMyContract
{
   [OperationContract]
   void Subscribe(int patientId);
   [OperationContract]
   void UnSubscribe(int patientId);
}

The next step is to define an events interface. This interface outlines expected methods the client must implement to be able to handle the events raised by the service. Note that the event handler has an attribute defining it as a OneWay call. This event is lab results for a patient have been entered into the system, and are of a critical nature.

public interface IPatientEvents
{
   [OperationContract(IsOneWay = true)]
   void LabResultsUpdated(int id, string text);
}

Then you simply create your client side class against the events interface. In this example the client is a WPF page class.

class PatientDetails : Page, IPatientEvents
{
     MyContractClient proxy;
	 
	 //other methods
}

The client will need to supply a context to the service so it can maintain an association on the channel. This is simply achieved through a constructor call when initialising the proxy. Also at this point we’re subscribing to the events for the given ID. This subscription can be client triggered this way. Alternatively certain actions can have the server subscribe the client server side. An example would be during a data retrieval call for a patient; the server forcing the setup up the subscription.

  InstanceContext context = new InstanceContext(this);
  
  proxy = new MyContractClient(context);

  proxy.Subscribe(id);

Now on the service we implement the same contract IMyContract we also define the signature of the delegate Action that will be raised as an event. Along with implementing the method that will attach the event handler to the appropriate client. The key thing to note here is the Current.GetCallbackChannel this is linking via the context the client supplied in the proxy initialisation.

[ServiceBehavior(InstanceContextMode
      = InstanceContextMode.PerCall)]
class PatientService : IMyContract
{
    static Action<int, string> m_EventLabs = delegate { };

    public void Subscribe(int patientId)
    {
        IPatientEvents subscriber = 
          OperationContext.Current.GetCallbackChannel<IPatientEvents>();

        m_EventLabs += subscriber.LabResultsUpdated;
    }
}

All that’s left now is to write the actual event firing code, that can be triggered after an appropriate event. In this example the firing takes place after the Patients Lab Results have been updated. Thereby allowing any other users currently viewing this patient’s details to be alerted.

public static void FireEvent(LabResult labResult)
{
    m_EventLabs(labResult.PatientId, labResult.ToString());
}

public bool SaveLabResults(LabResult lr)
{
    //save code...

    PatientService.FireEvent(labResult);
}

Back on the client we have defined a method that will be executed upon the event. Remember the service has attached the handler on the Subscribe() method.

public void LabResultsUpdated(int id, string text)
{
    this.displayAlerts.Text = text;
}

That is all that is required to have your client quickly and easily subscribe to and handle events. It’s thread-safe and efficient with the service doing all the hard work.

I will expand on [ServiceContract(CallbackContract … ] and [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] in future posts, and I’ll try to remember to update this post with direct links.

Just for some reference sake here are the objects used above that were not defined in the code blocks:

[DataContract]
public class Patient
{    [DataMember]
    public int PatientId { get; set; }
    //more members
}
[DataContract]
public class LabResult
{    [DataMember]
    public int LabResultId { get; set; }
    //more members
}
//displayAlerts is just a page <TextBlock> object
Advertisements