DEV Community

Cover image for πŸ“‘ SignalR: Real-Time Communication in .NET Core
Saurabh Khade
Saurabh Khade

Posted on

πŸ“‘ SignalR: Real-Time Communication in .NET Core

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
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Alternatively, you can install it via the NuGet Package Manager UI.

Nuget package manager window


πŸ“ 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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ” 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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… 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();
Enter fullscreen mode Exit fullscreen mode

πŸ”— Expose ChatHub publicly at /chathub:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapHealthChecks("/health");
    endpoints.MapHub<ChatHub>("/chatHub"); // Exposing SignalR Hub
});
Enter fullscreen mode Exit fullscreen mode

πŸŽ‰ 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);
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ž 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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ 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)