First of all, let's understand SignalR.
π SignalR is an open-source library built for .NET, designed to facilitate real-time communication in applications. Unlike the traditional request-response model, where the client sends a request and waits for a server response, SignalR enables two-way communication via a persistent connection.
π Think of WhatsApp! π¬ Whenever someone sends you a message, it instantly appears on your screen (server pushing data to the client). Similarly, when you send a message, it reaches the recipient immediately (client sending data to the server).
π Further Reading:
Now, letβs implement SignalR in .NET Core by building a simple backend for a chat application. π»
π οΈ Installing the SignalR NuGet Package
Install the latest SignalR package from NuGet using the following command:
dotnet add package Microsoft.AspNetCore.SignalR
π Alternatively, you can install it via the NuGet Package Manager UI.
π Creating a SignalR Hub
π The Hub is the central component of SignalR that enables real-time communication. It allows clients and servers to call each otherβs methods. Let's create a basic ChatHub:
using Microsoft.AspNetCore.SignalR;
namespace MySignalRProject.Hubs
{
// Create a hub class by inheriting SignalR Hub
public class ChatHub : Hub
{
// A server event triggered by the client
public async Task SendMessage(string message)
{
// Broadcast the message to all connected clients
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
}
π How does it work?
- Clients call
SendMessage(message)
, sending a message to the server. - The server then triggers
ReceiveMessage(message)
, sending it to all connected clients.
β Problem: Sending a message to everyone isn't always ideal.
β
Solution: We can use Groups to restrict message delivery.
π₯ Implementing Groups in SignalR
πΉ Groups allow clients to join specific chat rooms.
πΉ Only users in a group will receive messages sent to that group.
Hereβs how to implement it:
using Microsoft.AspNetCore.SignalR;
namespace MySignalRProject.Hubs
{
public class ChatHub : Hub
{
// Join a specific group
public async Task Connect(string groupId)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupId);
}
// Leave a group
public async Task Disconnect(string groupId)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupId);
}
// Send a message to a specific group
public async Task SendMessage(string groupId, string message)
{
await Clients.Group(groupId).SendAsync("ReceiveMessage", message);
}
}
}
β Now, only members of a specific group will receive messages sent to that group.
π Exposing the Hub via API
π οΈ Register SignalR services in Startup.cs
or Program.cs
:
services.AddSignalR();
π Expose ChatHub publicly at /chathub
:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
endpoints.MapHub<ChatHub>("/chatHub"); // Exposing SignalR Hub
});
π Once your API starts, the real-time chat hub will be accessible at /chathub
!
β‘ Making SignalR Type-Safe
π Potential Issue:
await Clients.Group(groupId).SendAsync("ReceiveMessage", message);
If a developer mistypes "ReceiveMessage"
as "ReceivedMessage"
or "receiveMessage"
, debugging the issue can be time-consuming.
β Solution: Use Strongly Typed Hubs to enforce correct method names.
1οΈβ£ Define Client Events in an Interface
namespace MySignalRProject.Hubs.Interfaces
{
public interface IClientChatHub
{
Task ReceiveMessage(string message);
Task ReceiveReaction(int userId, string reaction);
}
}
2οΈβ£ Implement a Strongly Typed Hub
using Microsoft.AspNetCore.SignalR;
using MySignalRProject.Hubs.Interfaces;
namespace MySignalRProject.Hubs
{
public class ChatHub : Hub<IClientChatHub>
{
public async Task Connect(string groupId)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupId);
}
public async Task Disconnect(string groupId)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupId);
}
public async Task SendMessage(string groupId, string message)
{
// No more typos! Method calls are now type-checked.
await Clients.Group(groupId).ReceiveMessage(message);
}
}
}
π Comparison: Strongly Typed vs. Traditional Approach
β Group.SendAsync("EventName")
|
β
Group.EventName
|
---|---|
Typos can break functionality β οΈ | No typo worries π |
No restrictions on event calls | Strict event control β |
No parameter validation | Parameter types are enforced π οΈ |
β Using strongly typed hubs reduces runtime errors and improves code maintainability.
π’ Injecting HubContext in Services
π€ Can we trigger SignalR events outside the Hub class?
π‘ Yes! By injecting IHubContext
, we can send messages from any service.
Example: Sending Messages from a Service Class
using Microsoft.AspNetCore.SignalR;
using MySignalRProject.Hubs;
using MySignalRProject.Hubs.Interfaces;
namespace MySignalRProject.Services
{
public class MyService
{
private readonly IHubContext<ChatHub, IClientChatHub> _hubContext;
public MyService(IHubContext<ChatHub, IClientChatHub> hubContext)
{
_hubContext = hubContext;
}
public async Task PerformSomeWork()
{
// Perform some logic...
var result = "Task completed β
";
// Notify clients in 'myGroup'
await _hubContext.Clients.Group("myGroup").ReceiveMessage(result);
}
}
}
π What is IHubContext<ChatHub, IClientChatHub>
?
-
ChatHub
: The SignalR Hub class. -
IClientChatHub
: The strongly-typed client event interface
β This approach allows you to send real-time updates from anywhere in your backend!
π― Conclusion
π Now you have a working real-time chat backend using SignalR in .NET Core!
π οΈ Key Takeaways:
β
SignalR enables real-time two-way communication
β
Using Groups ensures messages go to the right audience
β
Strongly typed hubs prevent typos & enforce method safety
β
HubContext allows triggering real-time updates from services
π Whatβs Next?
πΉ Implement authentication & authorization for enhanced security π
πΉ Add message persistence using a database π¦
πΉ Scale SignalR using Redis for distributed applications π
Happy coding! π»β¨
Top comments (0)