ASP.NET SignalR Hubs API Guide - .NET Client (C#)

Warning

This documentation isn't for the latest version of SignalR. Take a look at ASP.NET Core SignalR.

This document provides an introduction to using the Hubs API for SignalR version 2 in .NET clients, such as Windows Store (WinRT), WPF, Silverlight, and console applications.

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 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.

SignalR also offers a lower-level API called Persistent Connections. For an introduction to SignalR, Hubs, and Persistent Connections, or for a tutorial that shows how to build a complete SignalR application, see SignalR - Getting Started.

Software versions used in this topic

Previous versions of this topic

For information about earlier versions of SignalR, see SignalR Older Versions.

Questions and comments

Please leave feedback on how you liked this tutorial and what we could improve in the comments at the bottom of the page. If you have questions that are not directly related to the tutorial, you can post them to the ASP.NET SignalR forum or StackOverflow.com.

Overview

This document contains the following sections:

For a sample .NET client projects, see the following resources:

For documentation on how to program the server or JavaScript clients, see the following resources:

Links to API Reference topics are to the .NET 4.5 version of the API. If you're using .NET 4, see the .NET 4 version of the API topics.

Client setup

Install the Microsoft.AspNet.SignalR.Client NuGet package (not the Microsoft.AspNet.SignalR package). This package supports WinRT, Silverlight, WPF, console application, and Windows Phone clients, for both .NET 4 and .NET 4.5.

If the version of SignalR that you have on the client is different from the version that you have on the server, SignalR is often able to adapt to the difference. For example, a server running SignalR version 2 will support clients that have 1.1.x installed as well as clients that have version 2 installed. If the difference between the version on the server and the version on the client is too great, or if the client is newer than the server, SignalR throws an InvalidOperationException exception when the client tries to establish a connection. The error message is "You are using a version of the client that isn't compatible with the server. Client version X.X, server version X.X".

How to establish a connection

Before you can establish a connection, you have to create a HubConnection object and create a proxy. To establish the connection, call the Start method on the HubConnection object.

using (var hubConnection = new HubConnection("http://www.contoso.com/")) 
{
    IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
    stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
    await hubConnection.Start();
}

Note

For JavaScript clients you have to register at least one event handler before calling the Start method to establish the connection. This is not necessary for .NET clients. For JavaScript clients, the generated proxy code automatically creates proxies for all Hubs that exist on the server, and registering a handler is how you indicate which Hubs your client intends to use. But for a .NET client you create Hub proxies manually, so SignalR assumes that you will be using any Hub that you create a proxy for.

The sample code uses the default "/signalr" URL to connect to your SignalR service. For information about how to specify a different base URL, see ASP.NET SignalR Hubs API Guide - Server - The /signalr URL.

The Start method executes asynchronously. To make sure that subsequent lines of code don't execute until after the connection is established, use await in an ASP.NET 4.5 asynchronous method or .Wait() in a synchronous method. Don't use .Wait() in a WinRT client.

await connection.Start();
connection.Start().Wait();

Cross-domain connections from Silverlight clients

For information about how to enable cross-domain connections from Silverlight clients, see Making a Service Available Across Domain Boundaries.

How to configure the connection

Before you establish a connection, you can specify any of the following options:

  • Concurrent connections limit.
  • Query string parameters.
  • The transport method.
  • HTTP headers.
  • Client certificates.

How to set the maximum number of concurrent connections in WPF clients

In WPF clients, you might have to increase the maximum number of concurrent connections from its default value of 2. The recommended value is 10.

using (var hubConnection = new HubConnection("http://www.contoso.com/"))
{
    IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
    stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
    ServicePointManager.DefaultConnectionLimit = 10;
    await hubConnection.Start();
}

For more information, see ServicePointManager.DefaultConnectionLimit.

How to specify query string parameters

If you want to send data to the server when the client connects, you can add query string parameters to the connection object. The following example shows how to set a query string parameter in client code.

var querystringData = new Dictionary<string, string>();
querystringData.Add("contosochatversion", "1.0");
var connection = new HubConnection("http://contoso.com/", querystringData);

The following example shows how to read a query string parameter in server code.

public class StockTickerHub : Hub
{
    public override Task OnConnected()
    {
        var version = Context.QueryString["contosochatversion"];
        if (version != "1.0")
        {
            Clients.Caller.notifyWrongVersion();
        }
        return base.OnConnected();
    }
}

How to specify the transport method

As part of the process of connecting, a SignalR client normally negotiates with the server to determine the best transport that is supported by both server and client. If you already know which transport you want to use, you can bypass this negotiation process. To specify the transport method, pass in a transport object to the Start method. The following example shows how to specify the transport method in client code.

using (var hubConnection = new HubConnection("http://www.contoso.com/"))
{
    IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
    stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
    await hubConnection.Start(new LongPollingTransport());
}

The Microsoft.AspNet.SignalR.Client.Transports namespace includes the following classes that you can use to specify the transport.

The ForeverFrame transport is not included in this list because it is used only by browsers.

For information about how to check the transport method in server code, see ASP.NET SignalR Hubs API Guide - Server - How to get information about the client from the Context property. For more information about transports and fallbacks, see Introduction to SignalR - Transports and Fallbacks.

How to specify HTTP headers

To set HTTP headers, use the Headers property on the connection object. The following example shows how to add an HTTP header.

hubConnection = new hubConnection("http://www.contoso.com/");
connection.Headers.Add("headername", "headervalue");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await connection.Start();

How to specify client certificates

To add client certificates, use the AddClientCertificate method on the connection object.

hubConnection = new hubConnection("http://www.contoso.com/");
hubConnection.AddClientCertificate(X509Certificate.CreateFromCertFile("MyCert.cer"));
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await connection.Start();

How to create the Hub proxy

In order to define methods on the client that a Hub can call from the server, and to invoke methods on a Hub at the server, create a proxy for the Hub by calling CreateHubProxy on the connection object. The string you pass in to CreateHubProxy is the name of your Hub class, or the name specified by the HubName attribute if one was used on the server. Name matching is case-insensitive.

Hub class on server

public class StockTickerHub : Hub

Create client proxy for the Hub class

using (var hubConnection = new HubConnection("http://www.contoso.com/"))
{
    IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
    stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
    await hubConnection.Start();
}

If you decorate your Hub class with a HubName attribute, use that name.

Hub class on server

[HubName("stockTicker")]
public class StockTickerHub : Hub

Create client proxy for the Hub class

using (var hubConnection = new HubConnection("http://www.contoso.com/"))
{
    IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("stockTicker");
    stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
        Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
    await hubConnection.Start();
}

If you call HubConnection.CreateHubProxy multiple times with the same hubName, you get the same cached IHubProxy object.

How to define methods on the client that the server can call

To define a method that the server can call, use the proxy's On method to register an event handler.

Method name matching is case-insensitive. For example, Clients.All.UpdateStockPrice on the server will execute updateStockPrice, updatestockprice, or UpdateStockPrice on the client.

Different client platforms have different requirements for how you write method code to update the UI. The examples shown are for WinRT (Windows Store .NET) clients. WPF, Silverlight, and console application examples are provided in a separate section later in this topic.

Methods without parameters

If the method you're handling does not have parameters, use the non-generic overload of the On method:

Server code calling client method without parameters

public class StockTickerHub : Hub
{
    public void NotifyAllClients()
    {
         Clients.All.Notify();
    }
}

WinRT Client code for method called from server without parameters (see WPF and Silverlight examples later in this topic)

using (var hubConnection = new HubConnection("http://www.contoso.com/")) 
{
    IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
    stockTickerHub.On("notify", () =>
        // Context is a reference to SynchronizationContext.Current
        Context.Post(delegate
        {
            textBox.Text += "Notified!\n";
        }, null)
    );
    await hubConnection.Start();
}

Methods with parameters, specifying the parameter types

If the method you're handling has parameters, specify the types of the parameters as the generic types of the On method. There are generic overloads of the On method to enable you to specify up to 8 parameters (4 on Windows Phone 7). In the following example, one parameter is sent to the UpdateStockPrice method.

Server code calling client method with a parameter

public void BroadcastStockPrice(Stock stock)
{
    context.Clients.Others.UpdateStockPrice(stock);
}

The Stock class used for the parameter

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

WinRT Client code for a method called from server with a parameter (see WPF and Silverlight examples later in this topic)

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Methods with parameters, specifying dynamic objects for the parameters

As an alternative to specifying parameters as generic types of the On method, you can specify parameters as dynamic objects:

Server code calling client method with a parameter

public void BroadcastStockPrice(Stock stock)
{
    context.Clients.Others.UpdateStockPrice(stock);
}

The Stock class used for the parameter

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

WinRT Client code for a method called from server with a parameter, using a dynamic object for the parameter (see WPF and Silverlight examples later in this topic)

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

How to remove a handler

To remove a handler, call its Dispose method.

Client code for a method called from server

var updateStockPriceHandler = stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Client code to remove the handler

updateStockPriceHandler.Dispose();

How to call server methods from the client

To call a method on the server, use the Invoke method on the Hub proxy.

If the server method has no return value, use the non-generic overload of the Invoke method.

Server code for a method that has no return value

public class StockTickerHub : Hub
{
    public void JoinGroup(string groupName)
    {
        Groups.Add(Context.ConnectionId, groupName); 
    }
}

Client code calling a method that has no return value

stockTickerHubProxy.Invoke("JoinGroup", "SignalRChatRoom");

If the server method has a return value, specify the return type as the generic type of the Invoke method.

Server code for a method that has a return value and takes a complex type parameter

public IEnumerable<Stock> AddStock(Stock stock)
{
    _stockTicker.AddStock(stock);
    return _stockTicker.GetAllStocks();
}

The Stock class used for the parameter and return value

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

Client code calling a method that has a return value and takes a complex type parameter, in an ASP.NET 4.5 async method

var stocks = await stockTickerHub.Invoke<IEnumerable<Stock>>("AddStock", new Stock() { Symbol = "MSFT" });
foreach (Stock stock in stocks)
{
    textBox.Text += string.Format("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price);
}

Client code calling a method that has a return value and takes a complex type parameter, in a synchronous method

var stocks = stockTickerHub.Invoke<IEnumerable<Stock>>("AddStock", new Stock() { Symbol = "MSFT" }).Result;
foreach (Stock stock in stocks)
{
    textBox.Text += string.Format("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price);
}

The Invoke method executes asynchronously and returns a Task object. If you don't specify await or .Wait(), the next line of code will execute before the method that you invoke has finished executing.

How to handle connection lifetime events

SignalR provides the following connection lifetime events that you can handle:

  • Received: Raised when any data is received on the connection. Provides the received data.
  • ConnectionSlow: Raised when the client detects a slow or frequently dropping connection.
  • Reconnecting: Raised when the underlying transport begins reconnecting.
  • Reconnected: Raised when the underlying transport has reconnected.
  • StateChanged: Raised when the connection state changes. Provides the old state and the new state. For information about connection state values see ConnectionState Enumeration.
  • Closed: Raised when the connection has disconnected.

For example, if you want to display warning messages for errors that are not fatal but cause intermittent connection problems, such as slowness or frequent dropping of the connection, handle the ConnectionSlow event.

hubConnection.ConnectionSlow += () => Console.WriteLine("Connection problems.");

For more information, see Understanding and Handling Connection Lifetime Events in SignalR.

How to handle errors

If you don't explicitly enable detailed error messages on the server, the exception object that SignalR returns after an error contains minimal information about the error. For example, if a call to newContosoChatMessage fails, the error message in the error object contains "There was an error invoking Hub method 'contosoChatHub.newContosoChatMessage'." Sending detailed error messages to clients in production is not recommended for security reasons, but if you want to enable detailed error messages for troubleshooting purposes, use the following code on the server.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
App.MapSignalR(hubConfiguration);

To handle errors that SignalR raises, you can add a handler for the Error event on the connection object.

hubConnection.Error += ex => Console.WriteLine("SignalR error: {0}", ex.Message);

To handle errors from method invocations, wrap the code in a try-catch block.

try
{
    IEnumerable<Stock> stocks = await stockTickerHub.Invoke<IEnumerable<Stock>>("GetAllStocks");
    foreach (Stock stock in stocks)
    {
        Console.WriteLine("Symbol: {0} price: {1}", stock.Symbol, stock.Price);
    }
}
catch (Exception ex)
{
    Console.WriteLine("Error invoking GetAllStocks: {0}", ex.Message);
}

How to enable client-side logging

To enable client-side logging, set the TraceLevel and TraceWriter properties on the connection object.

using (var hubConnection = new HubConnection("http://www.contoso.com/"))
{
    hubConnection.TraceLevel = TraceLevels.All;
    hubConnection.TraceWriter = Console.Out;
    IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
    stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
    await hubConnection.Start();
}

WPF, Silverlight, and console application code samples for client methods that the server can call

The code samples shown earlier for defining client methods that the server can call apply to WinRT clients. The following samples show the equivalent code for WPF, Silverlight, and console application clients.

Methods without parameters

WPF client code for method called from server without parameters

stockTickerHub.On<Stock>("notify", () =>
    Dispatcher.InvokeAsync(() =>
        {
            SignalRTextBlock.Text += string.Format("Notified!");
        })
);

Silverlight client code for method called from server without parameters

stockTickerHub.On<Stock>("notify", () =>
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += "Notified!";
    }, null)
);

Console application client code for method called from server without parameters

stockTickerHubProxyProxy.On("Notify", () => Console.WriteLine("Notified!"));

Methods with parameters, specifying the parameter types

WPF client code for a method called from server with a parameter

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Dispatcher.InvokeAsync(() =>
        {
            textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
        })
);

Silverlight client code for a method called from server with a parameter

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Console application client code for a method called from server with a parameter

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Console.WriteLine("Symbol {0} Price {1}", stock.Symbol, stock.Price));

Methods with parameters, specifying dynamic objects for the parameters

WPF client code for a method called from server with a parameter, using a dynamic object for the parameter

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    Dispatcher.InvokeAsync(() =>
        {
            textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
        })
);

Silverlight client code for a method called from server with a parameter, using a dynamic object for the parameter

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Console application client code for a method called from server with a parameter, using a dynamic object for the parameter

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    Console.WriteLine("Symbol {0} Price {1}", stock.Symbol, stock.Price));