SignalR Realtime Application Cookbook
上QQ阅读APP看书,第一时间看更新

Adding a method to a Hub and counting the calls to it

This first recipe of the chapter is very simple, and in a way similar to the others we saw in Chapter 1, Understanding the Basics; however, the focus will be lesser on the process and the parts involved and more on specific Hub features. We'll learn a simple way to count how many times a Hub method is called by the connected clients.

Getting ready

Before writing the code of this recipe, we need to create a new empty web application, which we'll call Recipe05.

How to do it…

We're ready to actually start adding our SignalR bits by performing the following steps:

  1. Let's add a Hub called EchoHub. Behind the scenes, this action references a NuGet package called Microsoft.AspNet.SignalR, which then brings a few more packages.
  2. Then we add an OWIN Startup class named Startup, which contains just a simple app.MapSignalR() bootstrap call inside the Configuration() method.
  3. It is important to highlight the fact that it is recommended to add a Hub to the project before adding the Startup class, because the first action adds more package references to the project, and one of them (Microsoft.AspNet.SignalR.Core) contains the definition of the MapSignalR() extension method that we need to call from inside the Startup class.
  4. Let's edit the Hub file content to make it look like the following code:
    using System.Diagnostics;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    
    namespace Recipe05
    {
        [HubName("echo")]
        public class EchoHub : Hub
        {
            private static int _calls = 0;
            public void Hello()
            {
                Trace.WriteLine(string.Format("Calls: {0}",++_calls));
            }
        }
    }

What's important here? We need to make sure that the following steps are performed:

  • The class EchoHub is derived from Hub, which comes from Microsoft.AspNet.SignalR.Hubs, and makes the server-side SignalR API available to our class
  • The class is marked with the HubName attribute, which allows us to give the Hub a friendly name to be used by the clients; if we don't use the HubName attribute, the Hub name will be the same as the class name (in this case, it would be EchoHub)
  • We added a static int _calls field and used it to count how many times our exposed method will be called by any client
  • We exposed a public method called Hello() to increment the _calls static field on every call to it and then output its value into the debugger Output window, or in any Trace listener that we may want to configure

What we are expecting from the preceding code is a pretty naive way to count how many times the Hello() method is called. As mentioned in the introduction, we cannot assume anything about any specific instance of EchoHub being around (the actual default implementation will always create a new instance per call). The best way to count the calls across the clients is to use an external reference accessible by all the Hub instances, which will maintain the state across calls, for example, a static member as we do here. This is not a good strategy for production environments though, because static fields do not survive application recycles and this strategy does not scale horizontally in case of web farms. Still, this is just fine for the introductory goal of this recipe.

Let's move on with the client portion; to build that, we create an HTML page calling it index.html and add the following code to it:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Recipe05</title>
    <script src="Scripts/jquery-2.1.0.js"></script>
    <script src="Scripts/jquery.signalR-2.0.2.js"></script>
    <script src="/signalr/hubs"></script>
    <script>
        $(function () {
            var hub = $.connection.echo;

            $.connection.hub
                .start()
                .done(function () {
                    $('#send').click(function () {
                        hub.server.hello();
                    });
                });
        });
    </script>
</head>
<body>
    <button id="send">Send</button>
</body>
</html>

We basically kept what was there after the Visual Studio wizard execution, and then we added the <script> blocks to reference SignalR to address the dynamic hubs endpoint (/signalr/hubs), and eventually perform a connection to the server and a subsequent call to the exposed Hello() method. We explained these steps in greater detail in Chapter 1, Understanding the Basics, and we'll dig more into all the client-side steps in Chapter 3, Using the JavaScript Hubs Client API and Chapter 4, Using the .NET Hub's Client API. These will be recurring steps across the remaining recipes of this chapter.

We can now launch the application from Visual Studio. A browser window will open and the index.html page will be loaded. By clicking on the Send button on the page, the client will invoke the Hello() method of the EchoHub class on the client-side proxy, and the server-side code will reach the Trace.WriteLine(...); line inside the Hello() method of the EchoHub class. The _calls field will be incremented at every call, and we can verify that by either clicking on the button multiple times or by opening more browser windows pointing at the same address as the first one, and clicking on the Send button for all of them.

How it works…

The following are the most relevant points about what we just built:

  • Any public method exposed by a Hub class can be reached by any SignalR client
  • When any client calls a Hub method, a Hub instance is made available and the code from the called method is executed
  • In order to count the method calls across clients, we cannot rely on a specific instance of EchoHub being around all the time; so, for this example, we use the simplistic strategy of incrementing a static field

See also

If you are interested in more resilient ways to solve the problem of counting without having to worry about application recycles and horizontal scalability, please check out Chapter 7, Analyzing Advanced Scenarios.