Getting Started With SignalR Using ASP.NET Core - Dynamic Hub

Introduction

In this article, we'll learn how to use Dynamic Hub in ASPNET Core SignalR. The newly written SignalR allows you to write a dynamic type of T Hub. The benefit of using dynamic hub is that the method will resolve to the runtime so that you can register any method at runtime and call it.

This article is part of series of posts on SignalR using ASPNET Core. You can read:

  • Overview of New stack SIgnalR on ASPNET Core here
  • Getting Started With SignalR Using ASP.NET Core And Angular 5 here
  • Working with Azure service
  • Working with Xamarin
  • Working with streaming data here

This article demonstrates the following tasks:

  • Creating Dynamic Hub
  • Creating T of Hub
  • Consuming the Dynamic and T of Hub by the client
  • Demo

The source code is available at GitHub.

Before reading this article, I would highly recommend you to go through the other articles of the series which are mentioned above.

Creating Dynamic Hub

This is a base class for SignalR hubs that uses dynamic keyword to represent client invocations. Basically, the idea behind Dynamic Hub is hidden in the dynamic keyword. DynamicHub is inherited from Hub class. This is an abstract class and it is implementing the DynamicHubClients which contains all the dynamic type properties.

From MSDN

"The dynamic type enables the operations in which it occurs to bypass compile-time type checking. Instead, these operations are resolved at runtime. The dynamic type simplifies access to COM APIs such as the Office Automation APIs, and also to dynamic APIs such as IronPython libraries, and to the HTML Document Object Model (DOM)."

A Hub contains the following properties.

  • Clients : A clients caller abstraction for a hub, where you can push message to all connected clients
  • Context : A context abstraction for accessing information about the hub caller connection. It contains the connection and claims principle information.
  • Groups : A manager abstraction for adding and removing connections from groups. Ease of managing clients grouping.
  • Events : It provide the event notification on the client connected or disconnected so that you can override these events and add your custom login.

Dynamic Hub class

  1. //https://github.com/aspnet/SignalR/blob/dev/src/Microsoft.AspNetCore.SignalR.Core/DynamicHub.cs

  2. public abstract class DynamicHub : Hub  
  3.     {  
  4.         private DynamicHubClients _clients;  
  5.   
  6.         /// <summary>  
  7.         /// Gets or sets an object that can be used to invoke methods on the clients connected to this hub.  
  8.         /// </summary>  
  9.         public new DynamicHubClients Clients  
  10.         {  
  11.             get  
  12.             {  
  13.                 if (_clients == null)  
  14.                 {  
  15.                     _clients = new DynamicHubClients(base.Clients);  
  16.                 }  
  17.   
  18.                 return _clients;  
  19.             }  
  20.             set => _clients = value;  
  21.         }  
  22.     }  

DynamicHubClients Class

  1. //https://github.com/aspnet/SignalR/blob/dev/src/Microsoft.AspNetCore.SignalR.Core/DynamicHubClients.cs  
  2.   
  3. /// <summary>  
  4.     /// A class that provides <c>dynamic</c> access to connections, including the one that sent the current invocation.  
  5.     /// </summary>  
  6.     public class DynamicHubClients  
  7.     {  
  8.         private readonly IHubCallerClients _clients;  
  9.   
  10.         /// <summary>  
  11.         /// Initializes a new instance of the <see cref="DynamicHubClients"/> class.  
  12.         /// </summary>  
  13.         /// <param name="clients">A wrapped <see cref="IHubCallerClients"/> that is used to invoke methods.</param>  
  14.         public DynamicHubClients(IHubCallerClients clients)  
  15.         {  
  16.             _clients = clients;  
  17.         }  
  18.   
  19.         /// <summary>  
  20.         /// Gets an object that can be used to invoke methods on all clients connected to the hub.  
  21.         /// </summary>  
  22.         /// <returns>An object that can be used to invoke methods on the specified user.</returns>  
  23.         public dynamic All => new DynamicClientProxy(_clients.All);  
  24.   
  25.         /// <summary>  
  26.         /// Gets an object that can be used to invoke methods on all clients connected to the hub excluding the specified connections.  
  27.         /// </summary>  
  28.         /// <param name="excludedConnectionIds">A collection of connection IDs to exclude.</param>  
  29.         /// <returns>An object that can be used to invoke methods on the specified user.</returns>  
  30.         public dynamic AllExcept(IReadOnlyList<string> excludedConnectionIds) => new DynamicClientProxy(_clients.AllExcept(excludedConnectionIds));  
  31.   
  32.         /// <summary>  
  33.         /// Gets an object that can be used to invoke methods on the connection which triggered the current invocation.  
  34.         /// </summary>  
  35.         public dynamic Caller => new DynamicClientProxy(_clients.Caller);  
  36.   
  37.         /// <summary>  
  38.         /// Gets an object that can be used to invoke methods on the specified connection.  
  39.         /// </summary>  
  40.         /// <param name="connectionId">The connection ID.</param>  
  41.         /// <returns>An object that can be used to invoke methods.</returns>  
  42.         public dynamic Client(string connectionId) => new DynamicClientProxy(_clients.Client(connectionId));  
  43.   
  44.         /// <summary>  
  45.         /// Gets an object that can be used to invoke methods on the specified connections.  
  46.         /// </summary>  
  47.         /// <param name="connectionIds">The connection IDs.</param>  
  48.         /// <returns>An object that can be used to invoke methods.</returns>  
  49.         public dynamic Clients(IReadOnlyList<string> connectionIds) => new DynamicClientProxy(_clients.Clients(connectionIds));  
  50.   
  51.         /// <summary>  
  52.         /// Gets an object that can be used to invoke methods on all connections in the specified group.  
  53.         /// </summary>  
  54.         /// <param name="groupName">The group name.</param>  
  55.         /// <returns>An object that can be used to invoke methods.</returns>  
  56.         public dynamic Group(string groupName) => new DynamicClientProxy(_clients.Group(groupName));  
  57.   
  58.         /// <summary>  
  59.         /// Gets an object that can be used to invoke methods on all connections in all of the specified groups.  
  60.         /// </summary>  
  61.         /// <param name="groupNames">The group names.</param>  
  62.         /// <returns>An object that can be used to invoke methods on the specified user.</returns>  
  63.         public dynamic Groups(IReadOnlyList<string> groupNames) => new DynamicClientProxy(_clients.Groups(groupNames));  
  64.   
  65.         /// <summary>  
  66.         /// Gets an object that can be used to invoke methods on all connections in the specified group excluding the specified connections.  
  67.         /// </summary>  
  68.         /// <param name="groupName">The group name.</param>  
  69.         /// <param name="excludedConnectionIds">A collection of connection IDs to exclude.</param>  
  70.         /// <returns>An object that can be used to invoke methods.</returns>  
  71.         public dynamic GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds) => new DynamicClientProxy(_clients.GroupExcept(groupName, excludedConnectionIds));  
  72.   
  73.         /// <summary>  
  74.         /// Gets an object that can be used to invoke methods on connections in a group other than the caller.  
  75.         /// </summary>  
  76.         /// <returns>An object that can be used to invoke methods.</returns>  
  77.         public dynamic OthersInGroup(string groupName) => new DynamicClientProxy(_clients.OthersInGroup(groupName));  
  78.   
  79.         /// <summary>  
  80.         /// Gets an object that can be used to invoke methods on connections other than the caller.  
  81.         /// </summary>  
  82.         public dynamic Others => new DynamicClientProxy(_clients.Others);  
  83.   
  84.         /// <summary>  
  85.         /// Gets an object that can be used to invoke methods on all connections associated with the specified user.  
  86.         /// </summary>  
  87.         /// <param name="userId">The user ID.</param>  
  88.         /// <returns>An object that can be used to invoke methods.</returns>  
  89.         public dynamic User(string userId) => new DynamicClientProxy(_clients.User(userId));  
  90.   
  91.         /// <summary>  
  92.         /// Gets an object that can be used to invoke methods on all connections associated with all of the specified users.  
  93.         /// </summary>  
  94.         /// <param name="userIds">The user IDs.</param>  
  95.         /// <returns>An object that can be used to invoke methods.</returns>  
  96.         public dynamic Users(IReadOnlyList<string> userIds) => new DynamicClientProxy(_clients.Users(userIds));  
  97.     }  

Dynamic Chat Hub Class

In this hub, we are just sending a message to the client so in the case of Hub, we have to call SendAsync by passing the client method name which is listening to the client and message or other data to send. In case of Dynamic Hub, we can call any method which is bypassed by the compiler because it has dynamic keyword. But that method must be listened to on the client side to receive the data like Send method in above class.

  1. public class DynamicChatHub : DynamicHub  
  2.     {  
  3.         public async Task SendMessage(ChatMessage message)  
  4.         {  
  5.             await Clients.All.Send(message);  
  6.         }  
  7.     }  

I have created a separate service and component for dynamic hub which is consuming Dynamic Hub for communication and showing the messages. You will find the same in the attached project.

Creating T of Hub

Hub<T> is a base class for a strongly typed SignalR hub where T is type of client. The T typed can be used to invoke a method on clients connected. If you want your strongly typed custom methods, this is for you.

In a Hub, we have to tell in which method client is listening, so that while writing the name of the client method, you can avoid typos and other issues. 

We are going to create an Interface called IChatClient.

  1. public interface IChatClient  
  2.     {  
  3.         Task Send(string message);  
  4.     }  

TChatHub Class

This class is Inherited from strongly typed IChatClient Hub<IChatClient>. In this hub class, you can call IChatClient's methods on the client. This is the beauty of this Stack. This is just for pushing messages to the connected client. You can send a custom object to the client like we did in the streaming article earlier here.

  1. public class TChatHub : Hub<IChatClient>  
  2.     {  
  3.   
  4.         public Task Send(string message)  
  5.         {  
  6.             return Clients.All.Send(message);  
  7.         }  
  8.   
  9.     }  

Finally, we are done with Dynamic and T typed hub in SignalR.

Summary

In this, we have seen the working demo of dynamic and typed hub with signalR using ASP.NET Core and Angular 5. We have learned the following.

  • Dynamic Hub
    Dynamic Hub uses the dynamic keyword, we all know the beauty of dynamic keyword.

  • Typed Hub
    A Base class for a strongly typed SignalR hub.

References