Author : MD TAREQ HASSAN
Group in SignalR
- A group is a collection of connections associated with a name
- Groups in SignalR provide a method for broadcasting messages to specified subsets of connected clients
- A group can have any number of clients, and a client can be a member of any number of groups
- A connection can be a member of multiple groups. This makes groups ideal for something like a chat application, where each room can be represented as a group
- Connections can be added to or removed from groups via the
AddToGroupAsync
andRemoveFromGroupAsync
methods - Links:
Add Remove example
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has joined the group {groupName}.");
}
public async Task RemoveFromGroup(string groupName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has left the group {groupName}.");
}
Chat Group example
Models
public class ChatMessage
{
public string SenderName { get; set; }
public string Text { get; set; }
public DateTimeOffset SentAt { get; set; }
}
public class ChatRoom
{
public string OwnerConnectionId { get; set; }
}
Services
public interface IChatRoomService
{
Task<Guid> CreateRoom(string connectionId);
Task<Guid> GetRoomForConnectionId(string connectionId);
}
public class InMemoryChatRoomService : IChatRoomService
{
private readonly Dictionary<Guid, ChatRoom> _roomInfo
= new Dictionary<Guid, ChatRoom>();
public Task<Guid> CreateRoom(string connectionId)
{
var id = Guid.NewGuid();
_roomInfo[id] = new ChatRoom
{
OwnerConnectionId = connectionId
};
return Task.FromResult(id);
}
public Task<Guid> GetRoomForConnectionId(string connectionId)
{
var foundRoom = _roomInfo.FirstOrDefault(
x => x.Value.OwnerConnectionId == connectionId);
if (foundRoom.Key == Guid.Empty)
throw new ArgumentException("Invalid connection ID");
return Task.FromResult(foundRoom.Key);
}
}
Now, dependency injection in Startup class: services.AddSingleton<IChatRoomService, InMemoryChatRoomService>();
ChatHub.cs
public class ChatHub : Hub
{
private readonly IChatRoomService _chatRoomService;
public ChatHub(IChatRoomService chatRoomService)
{
_chatRoomService = chatRoomService;
}
public override async Task OnConnectedAsync()
{
var roomId = await _chatRoomService.CreateRoom(Context.ConnectionId);
await Groups.AddToGroupAsync(Context.ConnectionId, roomId.ToString());
await Clients.Caller.SendAsync(
"ReceiveMessage",
"Foo",
DateTimeOffset.UtcNow,
"bar");
await base.OnConnectedAsync();
}
public async Task SendMessage(string name, string text)
{
var roomId = await _chatRoomService.GetRoomForConnectionId(Context.ConnectionId);
var message = new ChatMessage
{
SenderName = name,
Text = text,
SentAt = DateTimeOffset.UtcNow
};
// Broadcast to all clients
await Clients.Group(roomId.ToString()).SendAsync(
"ReceiveMessage",
message.SenderName,
message.SentAt,
message.Text);
}
}