Previously I talked about how to set up the server side of SignalR for facilitating real-time communication. One of the parts that I don’t particularly care for in that default implementation is that the hub methods (the messages that are sent to connected clients) are identified by strings. Sure, you could create some string constants to identify them, but that still leaves you without IntelliSense / knowing what parameters are expected when sending those messages.
Strongly-Typed Hubs
Luckily, SignalR lets you define an interface that describes those message signatures, and then you can use it to have full IntelliSense to know what parameters are expected. This is especially useful when you’re injecting an IHubContext in to a different service.
// Define the hub methods | |
public interface IMyHub | |
{ | |
Task Publish(string message); | |
Task History(List<string> messages); | |
Task Whisper(string message); | |
} |
// inherit Hub<T>, where T is your interface defining the messages | |
public class MyHub: Hub<IMyHub> | |
{ | |
public async Task Publish(string message) | |
{ | |
// Send to everyone else | |
// Old way: | |
// await Clients.Others.SendAsync("Publish", message); | |
// New way: | |
await Clients.Others.Publish(message); | |
} | |
public async Task Whisper(string connectionIdTarget, string message) | |
{ | |
// Send to an individual client | |
// Old way: | |
// await Clients.Client(connectionIdTarget).SendAsync("Whisper", message); | |
// New way: | |
await Clients.Client(connectionIdTarget).Whisper(message); | |
} | |
} |
public class MyService | |
{ | |
// Use a second template parameter when defining the hub context to get the strongly typed hub context | |
private readonly IHubContext<MyHub, IMyHub> _myHub; | |
private readonly ISomeDatabaseRepository _db; | |
public MyService(IHubContext<MyHub, IMyHub> myHub, ISomeDatabaseRepository db) | |
{ | |
_myHub = myHub; | |
_db = db; | |
} | |
public async Task GetHistory(string connectionId) | |
{ | |
// Get the history from our pretend database | |
List<string> history = await _db.GetHistory(); | |
// Send the history to the client | |
// Old way: | |
// await _myHub.Clients.Client(connectionId).SendAsync("History", history); | |
// New way: | |
await _myHub.Clients.Client(connectionId).History(history); | |
} | |
} |
Using this method makes things a lot cleaner. You don’t have to use magic strings or make a constants file, and you don’t have to hold the parameter list in your brain while you’re working.
the strongly typed hub is a bit confusing. noticed that MyHub did not implement the History mehtod, but when invoking the History method defined in IMyHub, does signalR magically translate that into the old way of invoking the method?
Hey Kevin, interface defines server -> client communication messages, whereas methods on the hub itself are for client -> server communications.
Yep, SignalR is going to use those methods (and their names) to build up the message to send across the network. The IHub
thx Robert. that explains the key of strongly typed hub