Skip to content

SignalR + WCF + SOLID: Ways an SOA WCF Notification Service can use SignalR

November 11, 2013

This article explores 3 possible ways a WCF Real-Time Notification Service can utilize SignalR, while applying some of the SOLID principles.  My focus is entirely on pushing data to clients, rather than the chat type applications popular with SignalR demos. The WCF Real-Time Notification Service has the Single Responsibility of pushing data to clients.

Wouldn’t it be useful to have a WCF Real-Time (RT) Notification Service?  Then any other service in an SOA system can just call the WCF RT Notification Service via its Service Contract(s) and say “Push this data to the UI Clients listening for it”.  So easy!  With a WCF RT Notification Service using SignalR one can push data to all UI Clients – Web Clients written in HTML/JavaScript, WPF Clients, WP8 Clients, etc.

With an RT Notification Service available, Business Intelligence services can use it to push data to UI Clients exactly when server data changes, making a chart or graph change in client UIs.  Long running business processes can use it to notify UI Clients monitoring the process when process steps are complete or when human attention is needed.  I’m sure you’re aware of other UI Client use cases for a WCF RT Notification Service as well.

The 3 Ways a WCF RT Notification Service can use SignalR

  1. The WCF service uses the SignalR Persistent Connections feature to push data to clients.
  2. The WCF service uses one or more SignalR Hubs through a “direct reference” to the Hub on the server, calling the Hub’s “Client Methods” that invoke a method on the client as the means to push data.  This is the simplest implementation of the 3 Ways.
  3. The WCF service uses one or more Hubs as a client of SignalR, connecting to the Hub(s) via SignalR’s HTTP endpoint “connection”.  Then, through the HTTP connection as a client, the WCF service calls the Hub’s “Server Methods” as the means to push data.    My thanks to Christian Weyer for mentioning this idea in his useful PluralSight course “Introducing ASP.NET SignalR – Push Services with Hubs”.

What? “Client Methods”, “Server Methods”?  The terminology can be an obstacle.  If you start getting lost, please do a quick scan of “Introduction to SignalR”:  http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/introduction-to-signalr.  Look at the “What Is SignalR” section in the first 2 pages, and the “Connections and Hubs” section near the end of the document.

Which of these 3 Ways is best?  That depends on your requirements, the skill level of the developers working on the project now and in the future, and the relative importance in your organization of the tradeoff between reducing Total Cost of Ownership versus reducing Time to Market (often at odds with each other, but not always).  The goal of this article is to provide you with, and point you toward, much of the information you need to make that decision.  I have programmed the techniques described herein for the 2nd and 3rd Ways to ensure they work.  However, I have not yet worked with Persistent Connections.

The 1st Way for WCF Services to Utilize SignalR — Persistent Connections

SignalR provides 2 APIs – The Persistent Connections API and the Hub API.  The Persistent Connections API is a communication API that provides access to the low level SignalR communication protocol.  This is a connection based protocol, using messages and dispatching.  It will be familiar to developers knowledgeable in WCF which also uses messaging and dispatching.

As is the case when using any low level API, using Persistent Connections typically requires writing a lot more code than when using SignalR’s higher level Hub API.  The Hubs API is a remote invocation model which uses (and also hides the details of) the low level Persistent Connections.  All of the protocol negotiation required for the HTTP connection is automatically taken care of by Hubs.  As is all of the packaging of data transferred, and unpacking it on the client and converting it to client types.   With Persistent Connections the developer must write code to do much of this work themselves.

At the end of the document “Introduction to SignalR” Microsoft recommends that most apps use Hubs.  It says Persistent Connections should be used in the following cases:

  • It is required that the message format is specified by the developer, rather than by SignalR.
  • Developers are more comfortable working with a messaging/dispatching model, than the remote invocation model provided by Hubs.
  • An existing app that is implemented in a messaging model is being ported to SignalR.

However, keep in mind that messaging/dispatching is a very powerful means of communication, as WCF demonstrates.

This is a brief glimpse of how a WCF RT Notification Service can use SignalR’s Persistent Connections.  In the rest of this article I will focus on Hubs since they are the recommended approach and provide a higher level programming model.

Hubs and Terminology

First, please note that I use Camel Case names for methods and variables that are used on both the client and server since that is required by the SignalR documentation.

Hubs offer a rather simple high level interface.  And I must say from using it myself that it is easy to use, resulting in getting a Hub-based system working quickly.  Hubs are basically message brokers.  They can be a little confusing at first, so I’ll offer some assistance by using the simple mental model presented at the very top of the first page of the document at http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-net-client as follows:

“The SignalR Hubs API enables you to make remote procedure calls (RPCs) from a server to connected clients and from clients to the server. In server code, you define methods that can be called directly by clients, and you call methods that run on the client.  In client code, you define methods that can be called from the server, and you call methods that run on the server.  SignalR takes care of all of the client-to-server plumbing for you.”

Therefore, Hubs have 2 kinds of methods associated with them:  Client Methods and Server Methods.

  • Client Methods are callable by the server (from a Hub or other server side code that has a “direct reference” to a Hub).  They invoke methods on the client.  Think of Client Methods as ServerCallsClient methods, “scc” as a shorthand notation.  Client Methods transfer data from server to client via the method’s parameters.
  • Server Methods are callable by clients connected to a Hub via its HTTP endpoint (not a WCF endpoint).  They invoke methods on the server, i.e. the Hub.  Think of Server Methods as ClientCallsServer methods, or “ccs” for short.  Take note that server code outside of a Hub cannot call a Server Method defined on a Hub.  There is no way to do this.

Any client that is connected to a Hub via a SignalR HTTP connection can invoke a Server Method (ccs) defined on the Hub.  For example I could have a client invoke the IsHiEngineTemperatureAlarm() Server Method on a Hub.  That method will determine if there is a Hi Engine Temperature Alarm condition present (most likely by making a call outside the Hub object to get the info), and return the boolean result to the client.  This is a “normal” client-calls-server remote procedure call, similar to those encountered in WCF.

Later I will show a code example of the above IsHiEngineTemperatureAlarm() Server Method just for purposes of illustration.  However, in keeping with the Single Responsibility Principle (SRP) the RT Notification Service should only support one way notifications from the RT Notification Service to its clients.  Therefore, the Hubs used by this service should not have Server Methods unless they are used only for notification.  For example, we do not want to include a chat application in the RT Notification Service.

In this vein, I am sure that you can see the potential for a web of interrelated Client and Server Methods becoming unmanageable if not governed by SOLID principles.  Hubs are excellent mechanisms for separating concerns.  The Interface Separation Principle (ISP) is also important here:  Favor client-specific fine grained interfaces.  Use the SRP plus ISP in defining the Client and Server Methods associated with each Hub.  Keep them tightly focused on doing one thing, even if it means that you have a few more Hubs.  After all, a Hub is just a class available for encapsulating variation.

Anything on the server that has a “direct reference” to a Hub can invoke a Client Method (scc) method associated with that Hub.  This causes the client-side SignalR runtime to execute the specified method on the client, including supplying the client-side method with the data in the Client Method’s arguments.  For example the (scc) Client Method UpdateEngineTemperature(404.4) can be invoked via server code having a “direct reference” to the Hub associated with that Client Method.  The server-side invocation will cause the client-side SignalR runtime to invoke all registered client-side callbacks that implement that Client Method, and also provide each callback with the incoming argument value of 404.4.  The callback code can then take that 404.4 value and deposit it in some UI element that results in the temperature display changing.  Clients must register each callback that implements a Client Method to be informed by the client-side SignalR runtime that the Client Method has been invoked from the Hub (server).  Here your main focus should be on the concept of Client Methods, rather than the exact details of how they work.  Please see the SignalR documentation for how client-side programming works.  The key ideas to take away are — Via a “direct reference” to a Hub, server side things can call Client Methods (scc).  And Client Methods push data from server to client via the Client Method’s arguments.

The 2nd Way for WCF Services to Utilize SignalR – WCF has a “Direct Reference” to SignalR Hubs

The “direct references” to Hubs that I’ve been referring to are actually references to the InstanceContext of a Hub, of the type IHubContext.  They are not normal C# references to Hub classes, like the below InstrumentsHub class.

public class InstrumentsHub : Hub
{
    // Server Method (ccs).  Shown only as for illustration,
    // not to be implemented.
    public bool ccsIsHiEngineTemperatureAlarm()
    {
        return false;
    }
}

SignalR will not let you get C# references to Hub objects at runtime.  The SignalR runtime itself manages its Hub objects in such a way that a specific Hub may not be instantiated all the time.   Instead, SignalR provides the Hub InstanceContex for use by server code as a “direct reference” to a Hub.  Only though the InstanceContext can server code execute Client Methods associated with the Hub.

The following lines of C# code demonstrate how to get the InstanceContext of a Hub.  Assume that the WCF RT Notification Service contains the below code that gets executed periodically to update engine temperature displays on UI Clients.  The below C# code first gets the InstanceContext of a Hub.  Then it executes the Client Method called sccUpdateEngineTemperature(decimal temp).  Remember, the below code is within the WCF Service.


IHubContext m_InstrumentsHub =
              GlobalHost.ConnectionManager.GetHubContext<InstrumentsHub>();
// Server calls Client Method.
m_InstrumentsHub.Clients.All.sccUpdateEngineTemperature(404.4m);

Clients.All is a dynamic object whose members do not get bound until run time.  Therefore the dynamic Client Method sccUpdateEngineTemperature() cannot appear in the Instrument Hub source code and can only be accessed via a Hub InstanceContext.   As shown by the Instruments Hub code below, Client Methods are not normal C# members of the Hub class they are associated with.  The Instruments Hub has only one normal C# method on it, a Server Method called ccsIsHiEngineTemperatureAlarm().  The dynamic sccUpdateEngineTemperature() Client Method does not appear on the Hub’s class definition because it has been defined by the client, not the server.  Recall from the description of Hubs and Terminology: “In client code, you define methods that can be called from the server…”

public class InstrumentsHub : Hub
{
    // Server Method (ccs). Shown only as an example,
    // not to be implemented.
    public bool ccsIsHiEngineTemperatureAlarm()
    {
        return false;
    }

    // Note that no Dynamic Methods (Client Methods)
    // appear on the Hub, namely sccUpdateEngineTemperature().
}

Later I will show how the code of a WCF service can access dynamic Client Methods through a C# interface.

Also note that the Hub InstanceContext exposes only the dynamic objects used to call the Client Methods, and does not expose any Server Methods.  For example m_InstrumentsHub only exposes the “Clients” and “Groups” dynamic objects, which in turn expose other dynamic objects like “All”.   Thus, there is no way for server code outside a Hub class to call Server Methods.  Only a client can call Server Methods via the HTTP connection to the associated SignalR Hub.   For example, server code cannot call ccsIsHiEngineTemperatureAlarm() in the above class.

The fact that the members of dynamic objects are actually named by strings has far reaching effects.  Consider the following code where server code calls a Client Method:

m_InstrumentsHub.Clients.All.sccUpdateEngineTemperature(404.4m);

The complier will turn the Client Method name of sccUpdateEngineTemperature into a string when it generates code.  Instead of the above code, you can also use something like below to invoke a Client Method in source code from the server:

IClientProxy proxy = m_InstrumentsHub.Clients.All;
proxy.Invoke("sccUpdateEngineTemperature", 404.4m);

Note the fragility associated with Client Method Name Strings.  These strings are shared by the client and server code, and can easily get out of sync, i.e. changed on one place but not identically changed in the other place, thus breaking the code and causing errors.  I’ll cover this in more depth shortly.

Finally, to use GlobalHost.ConnectionManager.GetHubContext<T>() to get a Hub InstanceContext, the server code must be in the same AppDomain as the SignalR code (containing the Hubs, the HTTP Connection, and server side SignalR Runtime).  This means that the WCF RT Notification Service and the SignalR instance and its Hubs must share the same App Domain, typically by sharing the same Host process — the WCF Service Host.

To summarize so far, the first way for WCF services to utilize SignalR is to use Persistient Connections.  The 2nd Way for WCF services to utilize SignalR is for the WCF service to share its ServiceHost process with SignalR so the service has a reference to the InstanceContexts of Hubs to invoke Client Methods.

Here are some important things to note about a WCF service using SignalR via references to Hub InstanceContexts:

  1. Due to the Client Methods being implemented on dynamic objects and their late binding at runtime, the SignalR Hub does not provide a C# Interface or service contract for Client Methods that a WCF service can program against.
  2. The Client Method Name Strings are used on both the client (to register subscriptions for callbacks in JavaScript and .NET clients) and the server (as the names of the dynamic Client Methods).  Since this set of Client Method Name Strings is shared between the client and server they represent a significant source of variability (aka volatility) and fragility that can significantly increase costs, as follows:

Assume a developer has a Client Method called sccUpdateFoo(someValue) and has implemented one or more callbacks on a client using the string “sccUpdateFoo” to register the JavaScript or C# event handler callback(s) with the client SignalR runtime and/or proxy.  Then assume the developer changes the name of the Client Method on the server to UpdateFoo(someValue) since they are tired of dealing with my Hungarian Notation.  And, they neglect to update the related strings on the client from their original “sccUpdateFoo”.  This will break the code and there will be absolutely no indication of such other than the fact that UpdateFoo() will never be executed on the client.  It will silently fail.  I present more on this topic at the end of this article.

It is easy to fix 1 above, by providing a C# “Client Methods interface” for WCF to program against.  In addition to supporting interface based programming that simplifies the testability of the WCF service, this interface encapsulates the variability of Client Method names  This minimizes the footprint of where the Client Method Name Strings appear in the server code, reducing the fragility of the server code.  The “Client Methods interface” is implemented by a ClientMethodsProxy class whose members will be the only things on the server that call the actual Client Methods, either by Invoke() or via the Clients.All.someMethodName(), or one of its variants.  Thus, the ClientMethodsProxy is the only place where the Client Method Name Strings appear in the server side code.  Here’s the code:

public interface IInstrumentsHubClientMethods
{
    void SccUpdateEngineTemp(decimal measData);
}

public class InstrumentsHubClientMethodsProxy : IInstrumentsHubClientMethods
{
    IHubContext m_InstrumentsHubContext;

    public InstrumentsHubClientMethodsProxy(IHubContext hubContext)
    {
        m_InstrumentsHubContext = hubContext;
    }

    #region IInstrumentsHubClientMethods Members

    public void SccUpdateEngineTemperature(decimal measData)
    {
        m_InstrumentsHubContext.Clients.All.sccUpdateEngineTemperature(measData);
    }
    #endregion
}

The WCF service calls the Client Method through the proxy as follows:

IHubContext m_InstrumentsHub =
      GlobalHost.ConnectionManager.GetHubContext<InstrumentsHub>();
IInstrumentsHubClientMethods m_InstrumentsHubClientMethods =
      new InstrumentsHubClientMethodsProxy(m_InstrumentsHub);

m_InstrumentsHubClientMethods.SccUpdateEngineTemperature(measurement.Data);

With the above code, all of the Client Method Name Strings on the server are now encapsulated within the InstrumentsHubClientMethodsProxy class.  No longer are they sprinkled about the code of the WCF service.  And, the WCF service can program its calls to the Client Methods against an interface, as opposed to against a concrete implementation.  This is an application of the Dependency Inversion Principle (DIP):  Depend on abstractions rather than concrete implementations.  Above, the WCF service code depends upon the IIstrumentsHubClientMethods interface (an abstraction) rather than directly on strings.  Among other things, using the DIP greatly enhances the testability of the WCF service code apart from the Hub code.  Note that the 3rd Way presented below will also use the above interfaced programming technique, albeit with a different implementation of the IInstrumentsHubClientMethods interface.

How to deal with the Client Method Names on the client?  I’ll take that up at the end of this article.

The 3rd Way for WCF Services to Utilize SignalR – The WCF Service is a Client of SignalR Hubs via their HTTP Endpoint

In the list of the 3 Ways a WCF Service can use SignalR at the beginning of this article I described the 3rd Way as follows:

The WCF service uses one or more Hubs as a client of SignalR, connecting to the Hub(s) via SignalR’s HTTP endpoint “connection”.  Then, through the HTTP connection as a client, the WCF service calls the Hub’s “Server Methods” as the means to push data.

While the 3rd Way is modestly more complex than the 2nd Way, the 3rd Way does provide key differences over the 2nd Way which may be beneficial in some situations.  Most of the differences revolve around hosting, and while that is a large topic that I do not want to dive into, the following items are worth considering.

  • The 3rd Way probably hosts the WCF RT Notification Service separately from SignalR.  It is conceivable that both could be hosted in the same process and the WCF service would connect to SignalR as a client, rather than using its Hub’s InstanceContexts.  But this is unlikely.  What would be the gain over the 2nd Way?
  • When the WCF service is hosted separately from SignalR you gain scalability, flexibility, and plus process isolation.  Process isolation might pay off if the SignalR host process crashed, leaving the WCF service running and able to adapt to a crashed SignalR, perhaps by saving the incoming push requests to disk, etc.  There may be other benefits as well along this line of thinking.  Flexibility increases since you can now run the different hosts in the cloud, in IIS, in a Windows Service, or on a different server or virtual machine.  And scalability increases as well.
  • Another reason to host SignalR separately from the WCF RT Notification Service is to be able to use the SignalR host for multiple purposes – Use some Hubs for the WCF RT Notification Service, and other Hubs for other things like a chat facility, a Debug and Test Hub, etc.  In other words, consolidating all the SignalR hubs into a single host.
  • Finally, the 3rd Way’s use of SignalR provides an HTTP endpoint (although not a WCF endpoint) for the WCF service to connect to.  This is more loosely coupled than interacting via a “direct reference” to a Hub InstanceContex.

As you can see, breaking apart the hosting of the WCF Service from SignalR creates new opportunities and flexibility.  And this is achieved at a modest cost, as shown below.

Changes Required for the 3rd Way

Note that here are 2 kinds of SignalR clients in the 3rd Way:

  1. The same UI Clients as in the 2nd Way, which still connect to the InstrumentsHub to have their Client Methods invoked via the InstanceContext of the InstrumentsHub.  This has not changed from the 2nd Way.
  2. New in the 3rd Way is the WCF RT Notification Service connected as a client to a new Hub so it can invoke the new Hub’s Server Methods, which then invoke the Client Methods of the InstrumentsHub via the InstanceContext of the InstrumentsHub.  Here one Hub’s Server Methods invoke the Client Methods associated with a different Hub, the InstrumentsClientMethodsAsServerMethodsHub.

Here is the code for the new Hub that the WCF service now connects to as a client:

public class InstrumentsClientMethodsAsServerMethodsHub : Hub, IInstrumentsHubClientMethods
{
    IHubContext m_InstrumentsHub =
                        GlobalHost.ConnectionManager.GetHubContext<InstrumentsHub>();

    public void SccUpdateEngineTemperature(decimal measData)
    {
        m_InstrumentsHub.Clients.All.sccUpdateEngineTemperature(measData);
    }
}

Above, note that the new InstrumentsClientMethodsAsServerMethodsHub implements the IInstrumentsHubClientMethods interface that was used in the 2nd Way, for the same reasons that this interface was used there.  And, the Server Method on that Hub calls a Client Method associated with another Hub to do the data push.

As was done in the 2nd Way, in the code for the WCF RT Notification Service project contains a proxy that implements the IInstrumentsHubClientMethods interface.  Again this interface acts to 1) provide interface based programing per the Dependency Inversion Principle, and 2) consolidate all the Method Name Strings into one place as done in 2nd Way.  In this case, however, the strings are names of the Server Methods on the new InstrumentsClientMethodsAsServerMethodsHub.  This is due to the requirement of the SignalR .NET client runtime that Invoke() be used to run the Server Methods as shown below.  Here is the code for the new proxy:

public class InstrumentsHubClientMethodsProxyWay3 : IInstrumentsHubClientMethods
{
    IHubProxy m_InstrumentsHubClientMethodsAsServerMethodsProxy;

    public InstrumentsHubClientMethodsProxyWay3(IHubProxy hubProxy)
    {
        m_InstrumentsHubClientMethodsAsServerMethodsProxy = hubProxy;
    }

    #region IInstrumentsHubClientMethods Members

    public void SccUpdateEngineTemperature(decimal measData)
    {
       m_InstrumentsHubClientMethodsAsServerMethodsProxy.Invoke("SccUpdateEngineTemperature",
                                                                measData)
                                                        .Wait();
    }
    #endregion
}

The code in the WCF RT Notification Service that makes the connection to the new Hub via its HTTP endpoint and invokes the above proxy, as follows:

public class RtNotificationServiceWay3 : INrtInstrumentation
{
    IHubProxy m_InstrumentsHubClientMethodsAsServerMethodsProxy;
    IInstrumentsHubClientMethods m_HubClientMethods;

    public RtNotificationServiceWay3()
    {
        var signalRHubConnection = new HubConnection("http://localhost:1234/");
        m_InstrumentsHubClientMethodsAsServerMethodsProxy =
               signalRHubConnection.CreateHubProxy("InstrumentsClientMethodsAsServerMethodsHub");
        m_HubClientMethods =
                    new InstrumentsHubClientMethodsProxyWay3(
                                  m_InstrumentsHubClientMethodsAsServerMethodsProxy);
            // Start the connection.
            signalRHubConnection.Start().Wait();
        }

        m_HubClientMethods.SccUpdateEngineTemperature(404.4);
     }
}

As you can see, the differences between the Hub and WCF Service code between the 2nd Way and 3rd Way are small.

Additionally, now there needs to be 2 hosts instead of one – One for the WCF Service and one for SignalR.  The code to make 2 hosts is fairly simple as well, and I’ll leave that for you to figure out.  See the SignalR tutorials on self-hosting and hosting SignalR in an ASP.NET web site.

The 3rd Way provides much more flexibility at a modest cost over the 2nd Way.  However, the 3rd Way may have a higher risk of connection problems via the SignalR HTTP connection used by the WCF service.  This risk was not present in the 2nd Way.

So there you have it.  Which way is best for you?

One implementation detail to be aware is that both the 2nd and 3rd Ways can utilize a custom WCF ServiceBehavior that the WCF Service Host adds to its Service Description.Behaviors collection.  When WCF instantiates the WCF RT Notification Service instance this custom ServiceBehavior will cause either a Hub InstanceContext or a Hub HTTP Connection IHubProxy instance to be injected into a non-default constructor of the service instance.  This is way beyond the scope of this article, but is worth exploring as you will see from an in depth read of the SignalR documentation.

What about Those Fragile Client Method Name Strings?

In all of the 3 Ways of WCF using SignalR, the Client Method Name strings are shared between the client and server and represent a point of fragility.  At best it can be minimized by design, but it will still be there and it will grow with every new Client Method added to a Hub.  Here are ways to mitigate the risks these strings create:

  1. Always build a Client Method Name String Verification Tester, and run it after every build.  It should test each Client Method Name String used on the server and verify that method gets duly executed with the proper arguments on the client.
  2. Anton Kropp offers some insight into ways to detect mismatches of Client Method Name Strings on the server and client that result in silently failing broken code, plus other ideas to fix this problem.  See his blog at http://onoffswitch.net/strongly-typing-signalr/  for more info and code downloads.

Afterword

I really like SignalR, its 2 APIs, its ease of development, and the Real-Time Push capabilities it provides.  Thank you to Microsoft and the SignalR team for providing us with this framework.  I am looking forward to using it in the future to deliver significant value to users.

I hope you benefit as much from reading this article as I did researching and writing it.  Your comments are appreciated.

George Stevens
Creative Commons License

dotnetsilverlightprism blog by George Stevens is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. Based on a work at dotnetsilverlightprism.wordpress.com.

Advertisements
2 Comments
  1. dav permalink

    Hi This is really good. My requirment also the same, WCF service has to do notification to clients at some triggered times.

    thanks
    dav

Trackbacks & Pingbacks

  1. Professional Development 2/1 through 2/28/2016 | Code Ukemi

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: