--- title: ASP.NET Core SignalR .NET Client author: bradygaster description: Information about the ASP.NET Core SignalR .NET Client monikerRange: '>= aspnetcore-2.1' ms.author: wpickett ms.custom: mvc ms.date: 01/14/2020 uid: signalr/dotnet-client --- # ASP.NET Core SignalR .NET Client The ASP.NET Core SignalR .NET client library lets you communicate with SignalR hubs from .NET apps. [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/signalr/dotnet-client/sample) ([how to download](xref:index#how-to-download-a-sample)) The code sample in this article is a WPF app that uses the ASP.NET Core SignalR .NET client. ## Install the SignalR .NET client package The [Microsoft.AspNetCore.SignalR.Client](https://www.nuget.org/packages/Microsoft.AspNetCore.SignalR.Client) package is required for .NET clients to connect to SignalR hubs. # [Visual Studio](#tab/visual-studio) To install the client library, run the following command in the **Package Manager Console** window: ```powershell Install-Package Microsoft.AspNetCore.SignalR.Client ``` # [.NET CLI](#tab/net-cli) To install the client library, run the following command in a command shell: ```dotnetcli dotnet add package Microsoft.AspNetCore.SignalR.Client ``` --- ## Connect to a hub To establish a connection, create a `HubConnectionBuilder` and call `Build`. The hub URL, protocol, transport type, log level, headers, and other options can be configured while building a connection. Configure any required options by inserting any of the `HubConnectionBuilder` methods into `Build`. Start the connection with `StartAsync`. [!code-csharp[Build hub connection](dotnet-client/sample/signalrchatclient/MainWindow.xaml.cs?name=snippet_MainWindowClass&highlight=15-17,39)] ## Handle lost connection :::moniker range=">= aspnetcore-3.0" ### Automatically reconnect The can be configured to automatically reconnect using the `WithAutomaticReconnect` method on the . It won't automatically reconnect by default. ```csharp HubConnection connection= new HubConnectionBuilder() .WithUrl(new Uri("http://127.0.0.1:5000/chathub")) .WithAutomaticReconnect() .Build(); ``` Without any parameters, `WithAutomaticReconnect()` configures the client to wait 0, 2, 10, and 30 seconds respectively before trying each reconnect attempt, stopping after four failed attempts. Before starting any reconnect attempts, the `HubConnection` will transition to the `HubConnectionState.Reconnecting` state and fire the `Reconnecting` event. This provides an opportunity to warn users that the connection has been lost and to disable UI elements. Non-interactive apps can start queuing or dropping messages. ```csharp connection.Reconnecting += error => { Debug.Assert(connection.State == HubConnectionState.Reconnecting); // Notify users the connection was lost and the client is reconnecting. // Start queuing or dropping messages. return Task.CompletedTask; }; ``` If the client successfully reconnects within its first four attempts, the `HubConnection` will transition back to the `Connected` state and fire the `Reconnected` event. This provides an opportunity to inform users the connection has been reestablished and dequeue any queued messages. Since the connection looks entirely new to the server, a new `ConnectionId` will be provided to the `Reconnected` event handlers. > [!WARNING] > The `Reconnected` event handler's `connectionId` parameter will be null if the `HubConnection` was configured to [skip negotiation](xref:signalr/configuration#configure-client-options). ```csharp connection.Reconnected += connectionId => { Debug.Assert(connection.State == HubConnectionState.Connected); // Notify users the connection was reestablished. // Start dequeuing messages queued while reconnecting if any. return Task.CompletedTask; }; ``` `WithAutomaticReconnect()` won't configure the `HubConnection` to retry initial start failures, so start failures need to be handled manually: ```csharp public static async Task ConnectWithRetryAsync(HubConnection connection, CancellationToken token) { // Keep trying to until we can start or the token is canceled. while (true) { try { await connection.StartAsync(token); Debug.Assert(connection.State == HubConnectionState.Connected); return true; } catch when (token.IsCancellationRequested) { return false; } catch { // Failed to connect, trying again in 5000 ms. Debug.Assert(connection.State == HubConnectionState.Disconnected); await Task.Delay(5000); } } } ``` If the client doesn't successfully reconnect within its first four attempts, the `HubConnection` will transition to the `Disconnected` state and fire the event. This provides an opportunity to attempt to restart the connection manually or inform users the connection has been permanently lost. ```csharp connection.Closed += error => { Debug.Assert(connection.State == HubConnectionState.Disconnected); // Notify users the connection has been closed or manually try to restart the connection. return Task.CompletedTask; }; ``` In order to configure a custom number of reconnect attempts before disconnecting or change the reconnect timing, `WithAutomaticReconnect` accepts an array of numbers representing the delay in milliseconds to wait before starting each reconnect attempt. ```csharp HubConnection connection = new HubConnectionBuilder() .WithUrl(new Uri("http://127.0.0.1:5000/chathub")) .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.Zero, TimeSpan.FromSeconds(10) }) .Build(); // .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30) }) yields the default behavior. ``` The preceding example configures the `HubConnection` to start attempting reconnects immediately after the connection is lost. This is also true for the default configuration. If the first reconnect attempt fails, the second reconnect attempt will also start immediately instead of waiting 2 seconds like it would in the default configuration. If the second reconnect attempt fails, the third reconnect attempt will start in 10 seconds which is again like the default configuration. The custom behavior then diverges again from the default behavior by stopping after the third reconnect attempt failure. In the default configuration there would be one more reconnect attempt in another 30 seconds. If you want even more control over the timing and number of automatic reconnect attempts, `WithAutomaticReconnect` accepts an object implementing the `IRetryPolicy` interface, which has a single method named `NextRetryDelay`. `NextRetryDelay` takes a single argument with the type `RetryContext`. The `RetryContext` has three properties: `PreviousRetryCount`, `ElapsedTime` and `RetryReason`, which are a `long`, a `TimeSpan` and an `Exception` respectively. Before the first reconnect attempt, both `PreviousRetryCount` and `ElapsedTime` will be zero, and the `RetryReason` will be the Exception that caused the connection to be lost. After each failed retry attempt, `PreviousRetryCount` will be incremented by one, `ElapsedTime` will be updated to reflect the amount of time spent reconnecting so far, and the `RetryReason` will be the Exception that caused the last reconnect attempt to fail. `NextRetryDelay` must return either a TimeSpan representing the time to wait before the next reconnect attempt or `null` if the `HubConnection` should stop reconnecting. ```csharp public class RandomRetryPolicy : IRetryPolicy { private readonly Random _random = new Random(); public TimeSpan? NextRetryDelay(RetryContext retryContext) { // If we've been reconnecting for less than 60 seconds so far, // wait between 0 and 10 seconds before the next reconnect attempt. if (retryContext.ElapsedTime < TimeSpan.FromSeconds(60)) { return TimeSpan.FromSeconds(_random.NextDouble() * 10); } else { // If we've been reconnecting for more than 60 seconds so far, stop reconnecting. return null; } } } ``` ```csharp HubConnection connection = new HubConnectionBuilder() .WithUrl(new Uri("http://127.0.0.1:5000/chathub")) .WithAutomaticReconnect(new RandomRetryPolicy()) .Build(); ``` Alternatively, you can write code that will reconnect your client manually as demonstrated in [Manually reconnect](#manually-reconnect). :::moniker-end ### Manually reconnect :::moniker range="< aspnetcore-3.0" > [!WARNING] > Prior to 3.0, the .NET client for SignalR doesn't automatically reconnect. You must write code that will reconnect your client manually. :::moniker-end Use the event to respond to a lost connection. For example, you might want to automate reconnection. The `Closed` event requires a delegate that returns a `Task`, which allows async code to run without using `async void`. To satisfy the delegate signature in a `Closed` event handler that runs synchronously, return `Task.CompletedTask`: ```csharp connection.Closed += (error) => { // Do your close logic. return Task.CompletedTask; }; ``` The main reason for the async support is so you can restart the connection. Starting a connection is an async action. In a `Closed` handler that restarts the connection, consider waiting for some random delay to prevent overloading the server, as shown in the following example: [!code-csharp[Use Closed event handler to automate reconnection](dotnet-client/sample/signalrchatclient/MainWindow.xaml.cs?name=snippet_ClosedRestart)] ## Call hub methods from client `InvokeAsync` calls methods on the hub. Pass the hub method name and any arguments defined in the hub method to `InvokeAsync`. SignalR is asynchronous, so use `async` and `await` when making the calls. [!code-csharp[InvokeAsync method](dotnet-client/sample/signalrchatclient/MainWindow.xaml.cs?name=snippet_InvokeAsync)] The `InvokeAsync` method returns a `Task` which completes when the server method returns. The return value, if any, is provided as the result of the `Task`. Any exceptions thrown by the method on the server produce a faulted `Task`. Use `await` syntax to wait for the server method to complete and `try...catch` syntax to handle errors. The `SendAsync` method returns a `Task` which completes when the message has been sent to the server. No return value is provided since this `Task` doesn't wait until the server method completes. Any exceptions thrown on the client while sending the message produce a faulted `Task`. Use `await` and `try...catch` syntax to handle send errors. > [!NOTE] > Calling hub methods from a client is only supported when using the Azure SignalR Service in *Default* mode. For more information, see [Frequently Asked Questions](/azure/azure-signalr/signalr-resource-faq). ## Call client methods from hub Define methods the hub calls using `connection.On` after building, but before starting the connection. [!code-csharp[Define client methods](dotnet-client/sample/signalrchatclient/MainWindow.xaml.cs?name=snippet_ConnectionOn)] The preceding code in `connection.On` runs when server-side code calls it using the `SendAsync` method. [!code-csharp[Call client method](dotnet-client/sample/signalrchat/hubs/chathub.cs?name=snippet_SendMessage)] > [!NOTE] > While the hub side of the connection supports strongly-typed messaging, the client must register using the generic method with the method name. For an example, see . ## Error handling and logging Handle errors with a try-catch statement. Inspect the `Exception` object to determine the proper action to take after an error occurs. [!code-csharp[Logging](dotnet-client/sample/signalrchatclient/MainWindow.xaml.cs?name=snippet_ErrorHandling)] ## Additional resources * [Hubs](xref:signalr/hubs) * [JavaScript client](xref:signalr/javascript-client) * [Publish to Azure](xref:signalr/publish-to-azure-web-app) * [Azure SignalR Service serverless documentation](/azure/azure-signalr/signalr-concept-serverless-development-config)